From 6aae181d19f10a6e542bf1bb2e2494c7fe3259d9 Mon Sep 17 00:00:00 2001 From: ZDDC Date: Thu, 7 May 2026 09:42:45 -0500 Subject: [PATCH] docs: lead with folder purpose; surface RBAC + WORM on federal page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reference.html § 9: rewrite the canonical-folder tree so each line leads with what the folder is FOR (drafting space, "about to issue" lane, permanent record per counterparty, planned deliverables list, review queue) rather than mechanics. The lifecycle stage of a document is now visible from its location alone. Mechanics (lazy creation, case-fold matching, virtual user home, paired delete on issue) demoted to a single trailing paragraph so a reader can grasp the layout without needing to track them. federal.html: surface the access-control features that landed since the page was written — - Role-based access control as a first-class shipped feature, with the AC-2 / AC-3(7) mapping called out. - Verb-based least privilege (r/w/c/d/a) under AC-6, with the rc shape used by immutable archives flagged explicitly. - WORM enforcement on archive//{received,issued}/ under AU-9 and MP-5, including the at-the-WORM-folder grant pattern that lets doc controllers drop transmittals without giving them overwrite. - Cascade tracer (/.profile/effective-policy) under AC-3 reviewability. - OPA wire-format detail (input shape + cache TTL + fail-open). Move "Role-based access control" out of the "what you'd add for ATO" table now that it's shipped; replace with "Identity-provider role sync" — the integrator's job is wiring AD/Okta/EntraID groups into the existing role members: list, not building RBAC from scratch. Update "Policy export" to acknowledge the per-path tracer that already ships and frames the missing piece as the batch-export companion. Co-Authored-By: Claude Opus 4.7 (1M context) --- federal.html | 12 +++++--- reference.html | 75 +++++++++++++++++++++++++++++--------------------- 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/federal.html b/federal.html index e078620..43a73a8 100644 --- a/federal.html +++ b/federal.html @@ -54,8 +54,12 @@

These controls ship in every release today. No federal-specific build required to use them.

  • Hardened TLS posture. The TLS 1.2 cipher suite list, curve preferences, and HSTS header are pinned to the federal TLS guidance (NIST SP 800-52 Rev. 2). Only AEAD ciphers; only forward-secret key exchanges; only modern curves. Weak suites cannot be negotiated even if a client offers them.
  • -
  • Pluggable policy engine. Access decisions can be delegated to an external Open Policy Agent server with the customer's own audited Rego rules. The default in-process engine and the OPA-delegated engine speak the same wire format, so customers swap by setting one environment variable.
  • +
  • Role-based access control (NIST AC-2 / AC-3). A .zddc file declares named roles whose members are email patterns; permissions reference role names rather than pasting the same wildcards across every directory. Role definitions cascade and child files shadow ancestors for that subtree, so federation between an organisation-wide role definition and a project-specific override is built in. Maps cleanly to AC-3(7) "role-based" enforcement and AC-2 account-management workflows.
  • +
  • Verb-based least privilege (NIST AC-6). Every access decision evaluates one of five explicit verbs — r read, w overwrite, c create, d delete, a admin / edit ACL. A grant of rc means "this principal can read existing files and create new ones, but cannot modify or delete what's already there" — exactly the permission shape an immutable archive needs. Empty grants ("") are explicit denies that beat any other grant at the same level.
  • +
  • Write-once-read-many archive folders (NIST AU-9, MP-5). Files placed under archive/<party>/issued/ or archive/<party>/received/ are protected by a server-enforced verb mask: ancestor grants are reduced to read-only when crossing the WORM boundary, regardless of what an upstream .zddc says. A separate .zddc placed at the WORM folder itself can grant rc to specific principals (the doc-controller dropping a fresh transmittal) — that survives the mask. Root admins bypass the mask only as the deliberate escape hatch for mis-filed documents, with bypasses visible in the audit log.
  • +
  • Pluggable policy engine. Access decisions can be delegated to an external Open Policy Agent server with the customer's own audited Rego rules. The server POSTs request user, path, action, and the full .zddc cascade chain to /v1/data/zddc/access/allow; the customer's OPA returns allow/deny. Default in-process engine and OPA-delegated engine speak the same wire format, so customers swap by setting one environment variable. Failures fail closed by default; ZDDC_OPA_FAIL_OPEN=1 flips it for the rare case where availability outranks confidentiality.
  • Strict-least-privilege policy variant available out of the box. A parity-tested federal-mode Rego that enforces NIST AC-6 (parent denies are absolute; no leaf-level override) ships embedded in the binary. zddc-server --print-rego=federal emits it for use with the customer's OPA.
  • +
  • Cascade tracer for reviewers (NIST AC-3 reviewability). Admins can hit /.profile/effective-policy?path=<url> on a running server to see the resolved ACL chain at any path — every directory's grants, the role evaluation, and the final verb-set returned for the requesting user. A security reviewer can confirm "yes, this person has exactly these rights at this path" without reading the source. Helpful during accreditation and for incident response.
  • Structured audit logging. Every request is logged with the authenticated email, method, path, status, response size, and duration. Logs are JSON-line, ready for fluentd / Vector / SIEM pipelines.
  • Documented vulnerability-disclosure process. A SECURITY.md covering supported versions, reporting channel, response timeline, embargo workflow, and CVE assignment.
  • Cascading access control with explicit trust boundaries. The .zddc ACL model has documented invariants — root-only admin escalation gate, subtree-author authority limited to their subtree, default-deny when any .zddc exists. The access-control reference includes a five-minute verify-it recipe a security reviewer can run on their own deployment.
  • @@ -109,12 +113,12 @@ Today the proxy and zddc-server trust each other via network isolation. For federal, the recommended path is a signed forwarding token (JWT) — proxy signs each request with its private key, zddc-server verifies with the published public key. mTLS available as an alternative when the customer already operates a private CA. - Role-based access control - The current .zddc model grants access by email pattern. Federal AC-3(7) wants role-based grants populated from the customer's identity provider. The cascade syntax extends to express role allow/deny alongside email allow/deny; roles flow through the same OPA hook so customers running their own Rego pick them up automatically. + Identity-provider role sync + Role-based grants are already in the .zddc model — see the "Role-based access control" row above. What an integrator adds for federal is a sync layer that populates a role's members: list from the customer's identity provider (Active Directory groups, Okta, EntraID) on a schedule, so role membership tracks the IdP rather than being maintained by hand. The cascade format already accepts role members; this is the wiring that keeps them in sync. Policy export for change control - A command that walks the served tree and emits every directory's resolved access policy in JSON / Markdown / CSV. The change-control workflow checks the export into a Git repo; every .zddc change produces a diff that reviewers approve before deploy. NIST CM-3. + The cascade tracer (/.profile/effective-policy) already returns a resolved policy for any single path. What's missing is a batch export — a command that walks the entire served tree and emits every directory's resolved access policy in JSON / Markdown / CSV. The change-control workflow checks the export into a Git repo; every .zddc change produces a diff that reviewers approve before deploy. NIST CM-3. Code-signed tool fetches diff --git a/reference.html b/reference.html index bf96489..50490a9 100644 --- a/reference.html +++ b/reference.html @@ -917,42 +917,53 @@ date = 4DIGIT "-" 2DIGIT "-" 2DIGIT

    9. Project layout & transmittal workflow

    -

    A project is a directory containing a single .zddc file. The server materialises the canonical folders below on the first write into them — drop a .zddc in an empty directory, point zddc-server at it, and the layout comes to life as content arrives. Folder names are matched case-insensitively, so a manually-created Working/ is reused rather than shadowed by a new working/.

    +

    A ZDDC project mirrors the natural lifecycle of an engineering deliverable: drafted in private, lined up for issue, formally exchanged, kept as record. Each canonical folder maps to one of those stages, so file location alone tells you where a document is in its lifecycle. An operator only needs to create one file — a .zddc in an empty directory — and the rest of the layout populates as work happens.

    project/ - .zddc ← the only file an operator must create - working/ ← user-owned drafting workspace; mdedit - for markdown, browse for uploads. - Each authenticated user with access - sees a virtual <your-email>/ - subfolder; first write materialises it. - staging/ ← outbound-transmittal preparation. A - transmittal-named mkdir here also - creates a same-named drafting folder - under working/ as a sibling space. - reviewing/ ← virtual cross-reference of in-progress - review responses; never on disk. - Notes live in working/<rs-name>/. - archive/ ← formal record of issued/received - transmittals, organised by party. - {party-name}/ ← one per counterparty AND one for - ourselves; self-folder is symmetric. - mdl/ ← Master Deliverables List for that - party. Visiting the folder opens the - table editor (mdl.table.html). - Default schema is built into the - server; per-party overrides via a - tables: { mdl: ./mdl.table.yaml } - entry in the party's .zddc. - incoming/ ← that party's drop point. Auto-own - .zddc grants the creator of each new - subfolder full control. - received/ ← permanent record of incoming we've - accepted (WORM — write-once read-many). - issued/ ← permanent record of what we sent to - that party (WORM). + .zddc ← the only file an operator must create + + working/ ← Where staff draft. Each person has their own + subfolder here (named by email) and works on + documents in private until they're ready to + issue. Markdown opens in mdedit; arbitrary + files are dropped in via the browse tool. + + staging/ ← The "about to issue" lane. A folder in here + declares a planned outbound transmittal: its + name carries the planned issue date and tracking + number, so anyone can see what's queued up and + when. Files in staging are the finalised set; + drafting still happens in working/. + + reviewing/ ← One place to see everything we owe a response + on. Each row pairs an inbound submittal we've + accepted with our in-progress response draft — + saves staff from hunting across per-party + folders to find what's open. + + archive/ ← The permanent record. Everything we've ever + formally exchanged with a counterparty lives + here, organised one folder per party. + {party-name}/ ← One per counterparty; one for ourselves. + We treat our own organisation as just another + party so internal deliverables get the same + tracking treatment as external ones. + mdl/ ← The party's Master Deliverables List — + what they're going to produce, planned and + in-flight. Opens as a grid editor; rows are + individual yaml files, one per deliverable. + incoming/ ← Where that party drops things for us. Acts + as a quality-check buffer before content + becomes part of the permanent record. + received/ ← What we've accepted from that party. Immutable; + the historical record of inbound documents. + issued/ ← What we sent to that party. Immutable; the + historical record of outbound documents.
    + +

    Mechanics: folders materialise on first write, names match case-insensitively, the staging↔working pairing is automatic, the immutable folders enforce write-once via an ACL mask, and a virtual <your-email>/ entry under working/ creates your personal subfolder on first save. None of that needs to be in your head when you're using the system — drop files where the lifecycle says they go and the layout takes care of itself.

    +

    5-step transmittal workflow: