docs: lead with folder purpose; surface RBAC + WORM on federal page
All checks were successful
Deploy content to live site / deploy (push) Successful in 3s
All checks were successful
Deploy content to live site / deploy (push) Successful in 3s
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/<party>/{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) <noreply@anthropic.com>
This commit is contained in:
parent
f81fb4e769
commit
6aae181d19
2 changed files with 51 additions and 36 deletions
12
federal.html
12
federal.html
|
|
@ -54,8 +54,12 @@
|
|||
<p>These controls ship in every release today. No federal-specific build required to use them.</p>
|
||||
<ul class="feature-list">
|
||||
<li><strong>Hardened TLS posture.</strong> 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.</li>
|
||||
<li><strong>Pluggable policy engine.</strong> Access decisions can be delegated to an external <a href="https://www.openpolicyagent.org/" rel="noopener">Open Policy Agent</a> 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.</li>
|
||||
<li><strong>Role-based access control (NIST AC-2 / AC-3).</strong> A <code class="inline">.zddc</code> 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.</li>
|
||||
<li><strong>Verb-based least privilege (NIST AC-6).</strong> Every access decision evaluates one of five explicit verbs — <code class="inline">r</code> read, <code class="inline">w</code> overwrite, <code class="inline">c</code> create, <code class="inline">d</code> delete, <code class="inline">a</code> admin / edit ACL. A grant of <code class="inline">rc</code> 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 (<code class="inline">""</code>) are explicit denies that beat any other grant at the same level.</li>
|
||||
<li><strong>Write-once-read-many archive folders (NIST AU-9, MP-5).</strong> Files placed under <code class="inline">archive/<party>/issued/</code> or <code class="inline">archive/<party>/received/</code> 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 <code class="inline">.zddc</code> says. A separate <code class="inline">.zddc</code> placed at the WORM folder itself can grant <code class="inline">rc</code> 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.</li>
|
||||
<li><strong>Pluggable policy engine.</strong> Access decisions can be delegated to an external <a href="https://www.openpolicyagent.org/" rel="noopener">Open Policy Agent</a> server with the customer's own audited Rego rules. The server POSTs request user, path, action, and the full <code class="inline">.zddc</code> cascade chain to <code class="inline">/v1/data/zddc/access/allow</code>; 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; <code class="inline">ZDDC_OPA_FAIL_OPEN=1</code> flips it for the rare case where availability outranks confidentiality.</li>
|
||||
<li><strong>Strict-least-privilege policy variant available out of the box.</strong> A parity-tested federal-mode Rego that enforces NIST AC-6 (parent denies are absolute; no leaf-level override) ships embedded in the binary. <code>zddc-server --print-rego=federal</code> emits it for use with the customer's OPA.</li>
|
||||
<li><strong>Cascade tracer for reviewers (NIST AC-3 reviewability).</strong> Admins can hit <code class="inline">/.profile/effective-policy?path=<url></code> 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.</li>
|
||||
<li><strong>Structured audit logging.</strong> 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.</li>
|
||||
<li><strong>Documented vulnerability-disclosure process.</strong> A <a href="https://codeberg.org/VARASYS/ZDDC/src/branch/main/SECURITY.md">SECURITY.md</a> covering supported versions, reporting channel, response timeline, embargo workflow, and CVE assignment.</li>
|
||||
<li><strong>Cascading access control with explicit trust boundaries.</strong> The <code class="inline">.zddc</code> ACL model has documented invariants — root-only admin escalation gate, subtree-author authority limited to their subtree, default-deny when any <code class="inline">.zddc</code> exists. The <a href="https://codeberg.org/VARASYS/ZDDC/src/branch/main/zddc/README.md#access-control-the-zddc-cascade">access-control reference</a> includes a five-minute verify-it recipe a security reviewer can run on their own deployment.</li>
|
||||
|
|
@ -109,12 +113,12 @@
|
|||
<td style="padding: var(--spacing-sm); vertical-align: top;">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.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom: 1px solid var(--color-border);">
|
||||
<td style="padding: var(--spacing-sm); vertical-align: top;"><strong>Role-based access control</strong></td>
|
||||
<td style="padding: var(--spacing-sm); vertical-align: top;">The current <code class="inline">.zddc</code> 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.</td>
|
||||
<td style="padding: var(--spacing-sm); vertical-align: top;"><strong>Identity-provider role sync</strong></td>
|
||||
<td style="padding: var(--spacing-sm); vertical-align: top;">Role-based grants are already in the <code class="inline">.zddc</code> model — see the "Role-based access control" row above. What an integrator adds for federal is a sync layer that populates a role's <code class="inline">members:</code> 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.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom: 1px solid var(--color-border);">
|
||||
<td style="padding: var(--spacing-sm); vertical-align: top;"><strong>Policy export for change control</strong></td>
|
||||
<td style="padding: var(--spacing-sm); vertical-align: top;">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 <code class="inline">.zddc</code> change produces a diff that reviewers approve before deploy. NIST CM-3.</td>
|
||||
<td style="padding: var(--spacing-sm); vertical-align: top;">The cascade tracer (<code class="inline">/.profile/effective-policy</code>) 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 <code class="inline">.zddc</code> change produces a diff that reviewers approve before deploy. NIST CM-3.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: var(--spacing-sm); vertical-align: top;"><strong>Code-signed tool fetches</strong></td>
|
||||
|
|
|
|||
|
|
@ -917,42 +917,53 @@ date = 4DIGIT "-" 2DIGIT "-" 2DIGIT
|
|||
<!-- Section 9: Project layout & transmittal workflow -->
|
||||
<section id="transmittal-workflow">
|
||||
<h2>9. Project layout & transmittal workflow</h2>
|
||||
<p>A project is a directory containing a single <code>.zddc</code> file. The server materialises the canonical folders below on the first write into them — drop a <code>.zddc</code> in an empty directory, point <code>zddc-server</code> at it, and the layout comes to life as content arrives. Folder names are matched case-insensitively, so a manually-created <code>Working/</code> is reused rather than shadowed by a new <code>working/</code>.</p>
|
||||
<p>A ZDDC project mirrors the natural lifecycle of an engineering deliverable: <strong>drafted in private, lined up for issue, formally exchanged, kept as record</strong>. 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 <code>.zddc</code> in an empty directory — and the rest of the layout populates as work happens.</p>
|
||||
|
||||
<div class="code-block">
|
||||
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 <em><your-email>/</em>
|
||||
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 (<code>mdl.table.html</code>).
|
||||
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.
|
||||
</div>
|
||||
|
||||
<p style="margin-top: var(--spacing-md); font-size: 0.95em; color: var(--color-text-soft);"><em>Mechanics:</em> 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 <em><your-email>/</em> entry under <code>working/</code> 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.</p>
|
||||
|
||||
<p><strong>5-step transmittal workflow:</strong></p>
|
||||
|
||||
<table>
|
||||
|
|
|
|||
Loading…
Reference in a new issue