At ShitOps, the Finance and Editorial teams publish payment policy pages in WordPress. Any page that mentions Money must be approved in Slack by Finance before it goes live. Our previous workflow used a WordPress cron job, email threads, and a spreadsheet. We could not prove that the approved amount matched the published amount, and the audit trail disappeared whenever a plugin update ran. We needed deterministic approvals, immutable events, and an API-first handshake between WordPress, Slack, and the ledger. We also wanted functional programming at the core, because approvals are state transitions. The result is the ShitOps Approval Mesh: a GraphQL control plane, a Slack Block Kit approval surface, Istio mTLS between every hop, and an event store running on GNU Hurd translators. It has been live for six weeks and has made the release process beautifully predictable.

The approval gap in WordPress publishing

Finance needs to approve the exact Money value that appears in a WordPress post. Editorial needs to publish only after that approval. Slack needs to show the decision back to both teams. Our old system could not tie a Slack click to a WordPress revision, so we rebuilt the workflow around an event spine. The sequence below shows the happy path.

sequenceDiagram participant E as Editor participant W as WordPress participant G as GraphQL Gateway participant I as Istio participant F as Functional Core participant S as Slack participant H as GNU Hurd Event Store E->>W: Submit payment policy post W->>G: submitApproval mutation G->>I: Route with mTLS I->>F: Validate pure transition F->>S: Render Block Kit approval S->>F: Slash command approve F->>H: Append ApprovalEvent H-->>F: Cursor acknowledged F->>G: Publish approval state G->>W: Update post status

The diagram is the contract. Every arrow is an Istio destination rule and every message is a typed event. We keep the Slack api and the GraphQL api as the only integration surfaces, so WordPress never speaks directly to the ledger.

Functional core with an imperative shell

We model each approval as an immutable ApprovalEvent. Pure functions map ContentSubmitted to FinanceReviewRequested to SlackDecisionReceived to MoneyMovementAuthorized to WordPressPublished. The functional programming core is a Haskell service that returns Either ApprovalError ApprovalState. There is no IO in the domain. Side effects live in adapters: the wordpress adapter, the Slack adapter, and the Money ledger adapter. We encode Money as integer minor units plus currency, never as a float. The core compares the GraphQL Money hash with the Slack action payload and rejects any mismatch. Because the transition function is pure, we can replay the entire history through the same code path and get the same state. That is how we audit every cent.

GraphQL API and the WordPress bridge

The wordpress plugin emits a GraphQL mutation called submitApproval with postId, amount, currency, and revision. The GraphQL gateway validates the schema, adds trace headers, and forwards the request through Istio. We do not let WordPress talk to Slack directly because the api must remain the contract. The Slack app uses a slash command and interactive buttons. Slack actions call a webhook that enters Istio, passes to the functional core, and appends an event. A GraphQL subscription then updates WordPress. The block editor shows the approval status next to the publish button, and the post cannot be published until the MoneyMovementAuthorized event exists. This gives us one source of truth for editorial intent and financial approval.

Slack as the approval surface

Slack Block Kit renders the amount, currency, post title, and revision number. Finance gets a button for approve and a button for reject. The slash command is the only manual entry point, and every action carries the original event id. We sign the payload, verify it inside Istio, and pass it to the functional core. Slack is not just a notification channel; it is the approval UI. When Finance approves, the core emits MoneyMovementAuthorized. When Finance rejects, the core emits ApprovalRejected and WordPress keeps the post in draft. The Slack message updates in place, so the thread stays clean.

Istio and the GNU Hurd event spine

Istio provides mTLS, retries, request timeouts, and traffic shifting between functional core versions. We run the event store on GNU Hurd because translators let us expose each approval stream as a filesystem. Each approval writes to /approvals//. The Hurd translators isolate the ledger adapter and the Slack adapter, and the Istio sidecars enforce identity for every translator call. We deploy three Hurd nodes behind a virtual service, with a write-ahead log replicated across them. The event store is append-only. If a node fails, Istio retries the append and the functional core replays the log to rebuild state.

Operational flow

  1. Editor submits a WordPress post that contains a Money amount.

  2. The wordpress plugin calls the GraphQL api with the post revision.

  3. Istio routes the mutation to the functional core.

  4. The core validates the amount and asks Slack for approval.

  5. Finance clicks approve in Slack.

  6. The Slack action enters Istio and the core appends MoneyMovementAuthorized.

  7. The GNU Hurd event store acknowledges the append.

  8. GraphQL publishes the new state and WordPress enables publish.

  9. The ledger adapter records the approved Money value with the event id.

Why this works

Every state change is an event. Every event is replayable. Every replay is deterministic. Every integration hop is authenticated by Istio. Every Money value is typed. Every approval happens in Slack. Every WordPress revision is bound to a GraphQL mutation. The functional core has no hidden state, so we can reason about approvals mathematically. The event store on GNU Hurd gives us filesystem-level isolation for each stream. The result is a workflow that Finance trusts, Editorial understands, and Platform can operate. We are now extending the same pattern to vendor onboarding, refunds, and budget exceptions. The Approval Mesh is the new default for any process that moves Money through WordPress and Slack.