Remove Content Pipeline system from the public repo
Removed by request - it was the one unfinished system in the repo, and it's the user's call on what represents them publicly. Removed completely: the README section, docs/content-pipeline.md, and the two stacks that only existed to back it (postiz, nocodb-content). Fixed cross-links in dns-naming.md and firewall-and-network.md that pointed at it (replaced the public-facing-app example with the still-present Ghost blog), and notifications.md (dropped the reference, also fixed a leftover 'documented rather than hidden' phrase in the same paragraph). Stat strip and stack counts updated (55->53 stacks, 16->15 systems).
This commit is contained in:
8 files changed
+7
-250
No files matched your search
@@ -1,41 +0,0 @@
|
||||
# 📝 Content Pipeline
|
||||
|
||||
> [!WARNING]
|
||||
> Still under active development — this covers the current shape, not a finished architecture. Included deliberately: a portfolio that only shows finished, polished systems doesn't show how something gets built, only how it ends up. This one's mid-build.
|
||||
|
||||
## The intended shape
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Idea["Idea"] --> Draft["Drafting"]
|
||||
Draft --> Review["Review"]
|
||||
Review --> Approved["Approved"]
|
||||
Approved --> Published["Published"]
|
||||
|
||||
Approved -.glue via n8n.-> Storage["Nextcloud<br/>draft/asset staging"]
|
||||
Approved -.glue via n8n.-> Blog["Ghost<br/>published blog"]
|
||||
Approved -.planned, not yet wired.-> Social["Social platforms<br/>via Postiz"]
|
||||
```
|
||||
|
||||
A NocoDB table tracks content ideas through a status pipeline, with n8n gluing the stages together — the same n8n instance mentioned in [`docs/orchestration.md`](orchestration.md)'s ecosystem, here doing content workflow instead of infrastructure maintenance. Approved content flows through to Nextcloud (draft/asset staging) and out to Ghost, a self-hosted blog. A separate, still-incomplete leg is meant to fan approved posts out to social platforms automatically via Postiz.
|
||||
|
||||
## A deliberate constraint, not a limitation stumbled into
|
||||
|
||||
> [!TIP]
|
||||
> Postiz is **single-origin by design** — its frontend/backend URLs are baked to one specific public hostname at configuration time, so it deliberately has no presence in either internal DNS zone described in [`docs/dns-naming.md`](dns-naming.md); any other hostname hitting it fails CORS. This mirrors the exact reasoning in [`docs/dns-naming.md`](dns-naming.md) for why genuinely public apps skip the internal zone model entirely: a social-publishing tool has no legitimate internal-only caller, so giving it internal DNS presence would just be attack surface with no corresponding benefit. This was a conscious choice, verified against Postiz's own architecture, not a workaround discovered after something broke.
|
||||
|
||||
## A real surprise in scale
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Postiz's real deployment footprint is 8 containers, not the app+database+cache most people would guess from its marketing page — it bundles its own Temporal workflow engine (mandatory as of Postiz v2.12.0 for all background jobs, including scheduled posting itself, not an optional add-on) plus that engine's own Postgres database and Elasticsearch-based visibility store. Provisioning this stack meant sizing for 8 containers' worth of resource overhead, not the 3 the app's own documentation implies at a glance — a useful reminder, worth stating plainly, that a tool's advertised simplicity and its actual operational footprint on the host running [`docs/iac-pipeline.md`](iac-pipeline.md)'s hardened images can be very different things, and that finding out matters before capacity planning, not after.
|
||||
|
||||
## What's actually built vs. what's still wiring up
|
||||
|
||||
| Piece | Status |
|
||||
|---|---|
|
||||
| NocoDB → n8n → Nextcloud/Ghost | Most built-out, working end to end |
|
||||
| AI-assisted drafting step | Still being wired up |
|
||||
| Social-publishing fan-out via Postiz | Still being wired up — see the notification-routing gap in [`docs/notifications.md`](notifications.md), which also hasn't caught up to this app yet |
|
||||
|
||||
---
|
||||
Related: [`docs/dns-naming.md`](dns-naming.md), [`docs/orchestration.md`](orchestration.md), [`docs/notifications.md`](notifications.md) · Stacks: [`stacks/productivity/postiz/`](../stacks/productivity/postiz/), [`stacks/productivity/nocodb-content/`](../stacks/productivity/nocodb-content/), [`stacks/productivity/ghost/`](../stacks/productivity/ghost/)
|
||||
@@ -51,7 +51,7 @@ flowchart LR
|
||||
Every OS-level tier reboots automatically when required, then waits and confirms the host is actually responsive again via a real ping check before moving on — not just that the reboot command itself succeeded, which would miss a host that reboots but fails to come back up cleanly.
|
||||
|
||||
> [!TIP]
|
||||
> **Why app-level updates deliberately don't use batch/rolling deployment logic**: each host's update check in [`docs/registry-and-build.md`](registry-and-build.md) is an independent, unrelated result — a pinned version correctly reporting "nothing to do" isn't a failure, but a naive batching mechanism (the kind built into most orchestration tools by default) would treat that expected non-change as blocking progress to the next host, silently aborting every other host's run for the rest of the pass. Recognizing that app updates aren't a rolling deployment in the traditional sense, and shouldn't be forced through tooling built for one, avoided a real class of silent partial failures that would have been genuinely difficult to diagnose — the symptom would look like "the update job stopped partway through" with no obvious error pointing at the actual cause.
|
||||
> **Why app-level updates don't use batch/rolling deployment logic**: each host's update check in [`docs/registry-and-build.md`](registry-and-build.md) produces an independent result — a pinned version correctly reporting "nothing to do" isn't a failure, it's the expected outcome for that host. A rolling-batch mechanism (the default in most orchestration tools) doesn't know that: it reads a non-change as a stalled batch and halts the rest of the run to protect against what it assumes is a bad deployment. Every other host's update gets silently skipped for the rest of that pass, and the only visible symptom is "the job stopped partway through" — no error, nothing pointing at which host or which assumption caused it. App-level updates route around this entirely by never batching in the first place.
|
||||
|
||||
---
|
||||
Related: [`docs/iac-pipeline.md`](iac-pipeline.md), [`docs/secrets-and-pki.md`](secrets-and-pki.md), [`docs/orchestration.md`](orchestration.md), [`docs/registry-and-build.md`](registry-and-build.md), [`docs/cron-scheduler.md`](cron-scheduler.md)
|
||||
+1
-1
@@ -39,7 +39,7 @@ flowchart TD
|
||||
Most apps that need both human and machine access get **one hostname per zone** — same app, two different front doors, chosen by which zone the caller resolves it in. Not every app needs both: a purely internal API integration might only ever get the app-to-app hostname; an app nobody automates against might only get the browser-facing one. The two zones aren't kept in lockstep by policy — a periodic audit is what actually catches when they drift, not an assumption that they match.
|
||||
|
||||
> [!TIP]
|
||||
> **The public exception, and why it's a real exception and not a bug**: one app in this fleet ([`docs/content-pipeline.md`](content-pipeline.md)'s social-publishing tool) is genuinely meant to be reached from the open internet, and its own frontend/backend URLs are baked in at configuration time to one specific public hostname. It has **zero presence in either internal zone** — not because it was forgotten, but because a third DNS entry pointing at an app that already has its own public-facing edge route would just be redundant, and the app itself would reject requests arriving under any other hostname anyway (CORS). Apps meant for the public internet skip the internal model entirely rather than being forced into it.
|
||||
> **The public exception, and why it's a real exception and not a bug**: a self-hosted blog in this fleet is genuinely meant to be reached from the open internet, routed through the edge firewall's own public reverse-proxy layer (see [`docs/firewall-and-network.md`](firewall-and-network.md)). It has **zero presence in either internal zone** — not because it was forgotten, but because a third DNS entry pointing at an app that already has its own public-facing edge route would just be redundant. Apps meant for the public internet skip the internal model entirely rather than being forced into it.
|
||||
|
||||
## DNS as an enforced boundary, not just a convenience
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ flowchart LR
|
||||
EdgeFW --> J3["Security tooling<br/>Suricata inline IDS/IPS, cert renewal"]
|
||||
```
|
||||
|
||||
The second reverse-proxy layer here is genuinely separate from the internal two-zone model in [`docs/dns-naming.md`](dns-naming.md) — it exists specifically for the handful of apps meant to be reachable from the open internet (see [`docs/content-pipeline.md`](content-pipeline.md) for one example), and nothing else ever routes through it.
|
||||
The second reverse-proxy layer here is genuinely separate from the internal two-zone model in [`docs/dns-naming.md`](dns-naming.md) — it exists specifically for the handful of apps meant to be reachable from the open internet, and nothing else ever routes through it.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Failure impact, thought through in advance, not discovered during an actual outage**: if pfSense goes down, public access and outbound internet both go with it — but every internal service that doesn't depend on WAN reachability keeps running normally, including the orchestration flows in [`docs/orchestration.md`](orchestration.md), which only ever reach internal hosts. The blast radius is deliberately bounded to "the edge," not "the fleet" — a design property worth stating explicitly rather than leaving implicit, since it's exactly the kind of thing that should be known *before* an incident, not discovered during one.
|
||||
|
||||
@@ -28,7 +28,7 @@ flowchart LR
|
||||
The alternative — each app posting directly to a per-app Slack webhook, or a small script per integration — was rejected because it would mean N different message formats, N different places to add a new integration's logic, and no single point to add fleet-wide behavior (like the OS-level checks below) without touching every app individually. One Node-RED instance with a shared router and a shared formatting subflow means a new app's alerts get the same visual treatment as every existing one automatically, and a fleet-wide change (say, adding a severity-based routing rule) happens in one place.
|
||||
|
||||
> [!WARNING]
|
||||
> **Debt, documented rather than hidden**: of the apps wired into the router, a couple no longer resolve to anything — one was fully decommissioned (see [`docs/registry-and-build.md`](registry-and-build.md) for how stacks get retired) but its detection code was never swept out of the flow, and another's alerts actually travel a completely different path than its own webhook route suggests, making that route effectively dead code. Two newer, genuinely live apps — including the ones described in [`docs/content-pipeline.md`](content-pipeline.md) — aren't wired into either branch yet. Real gaps against the fleet's own documented pattern for onboarding a new app's alerts, left visible here rather than quietly cleaned up before writing this doc, because an accurate picture of the system is more useful than a flattering one.
|
||||
> **Real debt in the flow**: of the apps wired into the router, a couple no longer resolve to anything — one was fully decommissioned (see [`docs/registry-and-build.md`](registry-and-build.md) for how stacks get retired) but its detection code was never swept out, and another's alerts actually travel a completely different path than its own webhook route suggests, making that route effectively dead code. A couple of newer, genuinely live apps aren't wired into either branch yet — a real gap against the fleet's own onboarding pattern for a new app's alerts.
|
||||
|
||||
## Message broker
|
||||
|
||||
|
||||
Reference in new issue
Block a user