Compare commits
No commits in common. "6aae181d19f10a6e542bf1bb2e2494c7fe3259d9" and "e4149bf8cd397ef7b81819651f4078efe0f1c1fa" have entirely different histories.
6aae181d19
...
e4149bf8cd
3 changed files with 18 additions and 83 deletions
12
federal.html
12
federal.html
|
|
@ -54,12 +54,8 @@
|
|||
<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>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>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>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>
|
||||
|
|
@ -113,12 +109,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>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>
|
||||
<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>
|
||||
</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;">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>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: var(--spacing-sm); vertical-align: top;"><strong>Code-signed tool fetches</strong></td>
|
||||
|
|
|
|||
26
index.html
26
index.html
|
|
@ -175,21 +175,9 @@
|
|||
<p style="margin-top: var(--spacing-md);"><strong><code class="inline">zddc-server</code></strong> is a small Go binary purpose-built to serve ZDDC archives. <em>Any</em> web server gives you online mode; <code class="inline">zddc-server</code> adds things a generic web server can't:</p>
|
||||
|
||||
<ul class="feature-list">
|
||||
<li><strong>Access control via <code class="inline">.zddc</code> files.</strong> Behind a reverse proxy that authenticates users and sets an <code class="inline">X-Auth-Request-Email</code> request header, <code class="inline">zddc-server</code> consults YAML <code class="inline">.zddc</code> files at every directory along the path. The cascade walks root→leaf; the closest match wins. Five 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) gate every operation. An empty grant (e.g. <code class="inline">"*@vendor.com": ""</code>) is an explicit deny. Common shapes (paired open/closed projects, third-party-restricted vendor folders) are documented with worked examples in the <a href="https://codeberg.org/VARASYS/ZDDC/src/branch/main/zddc/README.md#access-control-the-zddc-cascade">access-control reference</a>. No database, no admin UI.</li>
|
||||
<li><strong>Roles for human-readable grants.</strong> A <code class="inline">.zddc</code> may declare named roles whose members are email patterns; permissions then reference the role name instead of pasting the same wildcard everywhere:
|
||||
<pre style="margin: 0.4rem 0;"><code>roles:
|
||||
qc-reviewers:
|
||||
members: ["*@quality.org", "alice@example.com"]
|
||||
acl:
|
||||
permissions:
|
||||
qc-reviewers: rwd
|
||||
"*@example.com": r</code></pre>
|
||||
Role definitions cascade like everything else; a child <code class="inline">.zddc</code> redefining the same role name shadows the ancestor for that subtree.</li>
|
||||
<li><strong>WORM archive folders.</strong> Anything under <code class="inline">archive/<party>/issued/</code> or <code class="inline">archive/<party>/received/</code> enforces write-once via a verb mask: ancestor grants are reduced to <code class="inline">r</code> only, while a <code class="inline">.zddc</code> placed at the WORM folder itself can still grant <code class="inline">rc</code> (create-but-not-overwrite) to specific principals — that's how a doc controller drops a fresh transmittal into the immutable record. Root admins (the <code class="inline">admins:</code> list in the root <code class="inline">.zddc</code>) bypass the mask as the deliberate escape hatch for mis-filed documents.</li>
|
||||
<li><strong>Lazy folder creation, case-fold matching.</strong> Drop a <code class="inline">.zddc</code> file into an empty directory and the canonical project layout (<code class="inline">working/</code>, <code class="inline">staging/</code>, <code class="inline">archive/<party>/{mdl,incoming,received,issued}/</code>) materialises on the first write into each path — never on bare reads. Folder names are matched case-insensitively, so an existing <code class="inline">Working/</code> is reused rather than shadowed by a new <code class="inline">working/</code> sibling. Each authenticated viewer sees a virtual <code class="inline">working/<your-email>/</code> entry; first write makes it real.</li>
|
||||
<li><strong>OPA-compatible policy decider.</strong> Federal and other regulated customers can swap the built-in evaluator for an external <a href="https://www.openpolicyagent.org/" rel="noopener">Open Policy Agent</a> server with their own audited Rego policies — set <code class="inline">ZDDC_OPA_URL</code> and the server POSTs the request's user, path, action, and the full <code class="inline">.zddc</code> cascade chain to <code class="inline">/v1/data/zddc/access/allow</code>. Decisions are cached per (user, path, action) with a configurable TTL (<code class="inline">ZDDC_OPA_CACHE_TTL</code>); failures fail closed by default (<code class="inline">ZDDC_OPA_FAIL_OPEN=1</code> flips it). The bundled NIST AC-6 strict-cascade preset is dumpable via <code class="inline">--print-rego=federal</code>. Default mode adds zero new dependencies; external mode is a configuration flip.</li>
|
||||
<li><strong>Access control via <code class="inline">.zddc</code> files.</strong> Behind a reverse proxy that authenticates users and sets an <code class="inline">X-Auth-Request-Email</code> request header, <code class="inline">zddc-server</code> consults YAML <code class="inline">.zddc</code> files in directories — cascading bottom-up; deeper rules override. Common shapes (paired open/closed projects + third-party-restricted vendor folders) are documented with worked examples in the <a href="https://codeberg.org/VARASYS/ZDDC/src/branch/main/zddc/README.md#access-control-the-zddc-cascade">access-control reference</a>. No database, no admin UI.</li>
|
||||
<li><strong>OPA-compatible policy decider.</strong> Federal and other regulated customers can swap the built-in evaluator for an external <a href="https://www.openpolicyagent.org/" rel="noopener">Open Policy Agent</a> server with their own audited Rego policies — set <code class="inline">ZDDC_OPA_URL</code> and the same <code class="inline">.zddc</code> files become inputs to your engine instead of ours. Wire format is OPA-canonical (<code class="inline">POST /v1/data/zddc/access/allow</code>). Default mode adds zero new dependencies; external mode is a configuration flip.</li>
|
||||
<li><strong>Designed for regulated environments.</strong> Hardened TLS (NIST SP 800-52 Rev. 2 cipher allowlist + HSTS), pluggable policy engine, federal-mode strict-least-privilege Rego shipping out of the box, structured audit logging, documented vulnerability-disclosure process. Specific federal-track work (FIPS-validated build, signed-token proxy↔server channel, code-signed tool fetches) is on a clear roadmap — see the <a href="federal.html">federal compliance page</a> for the supported deployment shape and what an integrator adds during ATO.</li>
|
||||
<li><strong>Cascade tracer for operators.</strong> Admins can hit <code class="inline">/.profile/effective-policy?path=<url></code> to see the resolved ACL chain at any path — every level's grants, the role evaluation, the final verb-set. Useful when a permission isn't behaving the way the operator expected.</li>
|
||||
<li><strong>Virtual <code class="inline">.archive</code> URL space.</strong> <code class="inline">GET /Project/.archive/123-XYZ.html</code> resolves to the canonical revision file at request time. Computed from filenames; no cache, no separate index file.</li>
|
||||
<li><strong>Per-request access logging</strong> keyed to the authenticated user; conservative HTTP timeouts; optional file-tee for offline audit (production deployments typically leave logs on stdout for the orchestrator's pipeline to handle).</li>
|
||||
<li><strong>TLS, ETags, conditional GET, CORS, autoindex.</strong> The mundane glue.</li>
|
||||
|
|
@ -209,14 +197,12 @@ Role definitions cascade like everything else; a child <code class="inline">.zdd
|
|||
<h3>Server: just run zddc-server</h3>
|
||||
<p class="when">The binary has the current-stable build of all five tools baked in at compile time. They appear automatically at the right paths under <code class="inline">ZDDC_ROOT</code>:</p>
|
||||
<ul class="install-points">
|
||||
<li><strong>archive.html</strong> and <strong>browse.html</strong> at every level (root, project, archive, party)</li>
|
||||
<li><strong>mdedit.html</strong> in any <code class="inline">working/</code> directory and its subtree</li>
|
||||
<li><strong>transmittal.html</strong> in any <code class="inline">staging/</code> directory and its subtree</li>
|
||||
<li><strong>classifier.html</strong> in any <code class="inline">working/</code>, <code class="inline">staging/</code>, or <code class="inline">archive/<party>/incoming/</code> subtree</li>
|
||||
<li><strong>mdl.table.html</strong> at every <code class="inline">archive/<party>/</code> — the per-party Master Deliverables List, served from a built-in default schema unless the party's <code class="inline">.zddc</code> declares a custom one</li>
|
||||
<li><strong>archive.html</strong> at every level (root, project, archive, vendor)</li>
|
||||
<li><strong>classifier.html</strong> in any <code class="inline">Incoming</code>, <code class="inline">Working</code>, or <code class="inline">Staging</code> directory and its subtree</li>
|
||||
<li><strong>mdedit.html</strong> in any <code class="inline">Working</code> directory and its subtree</li>
|
||||
<li><strong>transmittal.html</strong> in any <code class="inline">Staging</code> directory and its subtree</li>
|
||||
<li><strong>index.html</strong> (the project picker) at the deployment root</li>
|
||||
</ul>
|
||||
<p class="when" style="margin-top: 0.6rem;">Folder names are case-insensitive — <code class="inline">Working/</code>, <code class="inline">working/</code>, and <code class="inline">WORKING/</code> all match the <code class="inline">working/</code> rule.</p>
|
||||
<pre><code>ZDDC_ROOT=/srv/zddc ./zddc-server</code></pre>
|
||||
<p class="when" style="margin-top: 0.6rem;"><strong>To override a tool</strong> at any path: drop a real <code class="inline">.html</code> file there — that file wins over the baked-in version. <strong>To pin a different version</strong>, write an <code class="inline">apps:</code> entry in any <code class="inline">.zddc</code> file along the path:</p>
|
||||
<pre><code># <project>/.zddc
|
||||
|
|
|
|||
|
|
@ -914,57 +914,19 @@ date = 4DIGIT "-" 2DIGIT "-" 2DIGIT
|
|||
<p>Search the deliverable's own tracking number across all transmittal folders in <code>issued/</code> and <code>received/</code>. Every folder containing that number is part of its history: which package first carried it, what the response was, which resubmittal carried the revision, and what the final outcome was.</p>
|
||||
</section>
|
||||
|
||||
<!-- Section 9: Project layout & transmittal workflow -->
|
||||
<!-- Section 9: Transmittal workflow -->
|
||||
<section id="transmittal-workflow">
|
||||
<h2>9. Project layout & transmittal workflow</h2>
|
||||
<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>
|
||||
<h2>9. Transmittal workflow</h2>
|
||||
<p>Each third party (client, contractor, vendor, etc.) has a separate subfolder. All communication with that party lives in one place.</p>
|
||||
|
||||
<div class="code-block">
|
||||
project/
|
||||
.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.
|
||||
{party-name}/
|
||||
incoming/ ← transmittals received from party, awaiting acceptance
|
||||
received/ ← permanent record of accepted transmittals from party
|
||||
issued/ ← your copies of transmittals sent to party
|
||||
</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>
|
||||
<p><strong>5-step workflow:</strong></p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
|
|
@ -1012,15 +974,6 @@ project/
|
|||
<div class="highlight-box" style="margin-top: var(--spacing-lg);">
|
||||
<p><strong>What SHA-256 gives you:</strong> Mathematical fingerprint of file contents. Single byte change → hash changes. When acknowledgment records hashes, you can verify years later that the file is identical to what was transmitted.</p>
|
||||
</div>
|
||||
|
||||
<h3>Drafting a response transmittal</h3>
|
||||
<p>Submittals from counterparties (tracking numbers containing <code>-SUB-</code> at status <code>IFR</code> or <code>IFA</code>) require a response transmittal whose status starts with <code>RS</code> (RSA, RSB, RSC, …). The drafting flow uses the same staging↔working pairing as a fresh outbound:</p>
|
||||
<ol>
|
||||
<li>Create <code>staging/<YYYY-MM-DD>_<tracking> (RSA) - <title>/</code>. The date is the planned issue (response-due) date.</li>
|
||||
<li>The server mirrors the folder name into <code>working/</code>; reviewer notes and the response payload are drafted there.</li>
|
||||
<li>The <code>reviewing/</code> virtual surface lists each in-progress response paired with the source submittal in <code>archive/<party>/received/</code>.</li>
|
||||
<li>When the response is ready, files move from <code>working/</code> into <code>staging/</code> for sign-off, then through the standard 5-step transmittal flow into <code>archive/<party>/issued/</code>. Both the staging and working folders are deleted at issue time.</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<!-- Section 10: Tools -->
|
||||
|
|
|
|||
Loading…
Reference in a new issue