diff --git a/index.html b/index.html index 7c3f71c..fa96275 100644 --- a/index.html +++ b/index.html @@ -134,7 +134,7 @@
zddc-server is a small Go binary purpose-built to serve ZDDC archives. Any web server gives you online mode; zddc-server adds things a generic web server can't:
.zddc file into an empty directory and the canonical project layout (working/, staging/, archive/<party>/{mdl,incoming,received,issued}/) materialises on the first write into each path — never on bare reads. Folder names are matched case-insensitively, so an existing Working/ is reused rather than shadowed by a new working/ sibling. Each authenticated viewer sees a virtual working/<your-email>/ entry; first write makes it real..zddc file into an empty directory and the canonical project layout (archive/<party>/{mdl,rsk,incoming,received,issued,working,staging,reviewing}/) materialises on the first write into each path — never on bare reads. archive/ is the only physical project-root child; ssr, mdl, rsk, working, staging, reviewing sit beside it as virtual aggregators that synthesise listings across parties. Folder names are matched case-insensitively, so an existing Working/ is reused rather than shadowed by a new working/ sibling. Each authenticated viewer sees a virtual archive/<party>/working/<your-email>/ entry; first write makes it real..archive URL space. GET /Project/.archive/123-XYZ.html resolves to the canonical revision file at request time. Computed from filenames; no cache, no separate index file..zddc files. Behind a reverse proxy that authenticates users and sets an X-Auth-Request-Email request header, zddc-server consults YAML .zddc files at every directory along the path. The cascade walks root→leaf; the closest match wins. Five verbs (r read, w overwrite, c create, d delete, a admin / edit ACL) gate every operation. An empty grant (e.g. "*@vendor.com": "") is an explicit deny. A subtree that wants to start fresh — vendor folder, regulated workspace — can declare inherit: false to fence off ancestor grants and roles, then list the principals it does want. Common shapes (paired open/closed projects, third-party-restricted vendor folders) are documented with worked examples in the access-control reference. No database, no admin UI..zddc may declare named roles whose members are email patterns; permissions then reference the role name instead of pasting the same wildcard everywhere:
diff --git a/reference.html b/reference.html
index db7b593..eed046d 100644
--- a/reference.html
+++ b/reference.html
@@ -1001,47 +1001,69 @@ date = 4DIGIT "-" 2DIGIT "-" 2DIGIT
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. The browse tool handles everything here
- — file management, in-place markdown editing
- with live preview, on-demand format conversion.
+ archive/ ← The only PHYSICAL project-root directory.
+ Everything party-scoped (records, lifecycle,
+ immutable archives) lives uniformly under
+ archive/<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. Opens as
+ a grid editor; rows are individual .yaml
+ files, one per deliverable.
+ rsk/ ← Risk register for this party. Same grid
+ editor as mdl/.
+ incoming/ ← Where the counterparty drops things for us.
+ A quality-check buffer before content
+ becomes part of the permanent record.
+ received/ ← What we've accepted from that party.
+ Immutable (WORM); the historical record
+ of inbound documents.
+ issued/ ← What we sent to that party. Immutable
+ (WORM); the historical record of outbound
+ documents.
+ working/ ← Where this party's staff draft. Each
+ person has a private subfolder here
+ (named by email) and iterates until ready
+ to commit. The browse tool handles
+ everything here.
+ <your-email>/ ← Your private workspace under THIS
+ party. Fenced (inherit:false) by the
+ auto-own .zddc, so other team members
+ only see what you explicitly share.
+ staging/ ← The "about to issue" lane for this party.
+ Drop files here and the project_team
+ gives them up — only the doc-controller
+ can change a file after it lands. Each
+ sub-folder declares a planned outbound
+ transmittal (name carries the date +
+ tracking number).
+ reviewing/ ← One place per party to see everything we
+ owe a response on. Folders are scaffolded
+ by Plan Review, each pairing an inbound
+ submittal in received/ with the in-progress
+ response draft.
- 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.
+ ssr/ ← Virtual aggregator: tables rollup of every
+ party's ssr.yaml row, with a synthesised
+ $party column. Never on disk.
+ mdl/ ← Virtual aggregator: tables rollup of every
+ party's mdl/*.yaml deliverables.
+ rsk/ ← Virtual aggregator: tables rollup of every
+ party's rsk/*.yaml risk rows.
+ working/ ← Virtual aggregator: browse folder-nav listing
+ parties with non-empty content in working/.
+ Per-party clicks 302-redirect to the canonical
+ archive/<party>/working/.
+ staging/ ← Same shape as working/ for the staging slot.
+ reviewing/ ← Same shape as working/ for the reviewing slot.
- 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.
Mechanics: folders materialise on first write, names match case-insensitively, the WORM zones (received/, issued/) enforce write-once via an ACL mask, and the six top-level aggregators (ssr/mdl/rsk/working/staging/reviewing) are virtual — they never materialise on disk but show up in listings, computed from archive/<party>/… at request time. Mkdir at the project root is restricted to archive + system names (_/.-prefixed) so the virtual names can never be shadowed by a physical folder. Drop files where the lifecycle says they go and the layout takes care of itself.
The in-flight ratchet. working/ → staging/ → issued/ is a one-way handoff. project_team iterates freely in working/ (the auto-own-fenced subfolder gives each user a private rwcda workspace). When they drop a file into staging/ their access downgrades to cr — they can drop more, but only the document_controller can change what's already there. When DC publishes to issued/, the WORM mask downgrades even DC to cr (write-once). Each handoff is a commitment by permission-loss.
5-step transmittal workflow:
@@ -1093,12 +1115,12 @@ project/Submittals from counterparties (tracking numbers containing -SUB- at status IFR or IFA) require a response transmittal whose status starts with RS (RSA, RSB, RSC, …). The drafting flow uses the same staging↔working pairing as a fresh outbound:
Submittals from counterparties (tracking numbers containing -SUB- at status IFR or IFA) require a response transmittal whose status starts with RS (RSA, RSB, RSC, …). The flow walks the ratchet:
staging/<YYYY-MM-DD>_<tracking> (RSA) - <title>/. The date is the planned issue (response-due) date.working/; reviewer notes and the response payload are drafted there.reviewing/ virtual surface lists each in-progress response paired with the source submittal in archive/<party>/received/.working/ into staging/ for sign-off, then through the standard 5-step transmittal flow into archive/<party>/issued/. Both the staging and working folders are deleted at issue time.archive/<party>/received/<tracking>/ and pick Plan Review. The server scaffolds a workflow folder at archive/<party>/reviewing/<tracking>/ — its .zddc carries a received_path pointer back to the canonical submittal and a planned response date.archive/<party>/working/<your-email>/ is where reviewer notes and the response payload are drafted. The reviewing/ virtual aggregator at the project root surfaces all open reviews across parties.working/ into archive/<party>/staging/ for sign-off. Project team's permission on staged files downgrades to cr — the doc controller takes over.staging/ into archive/<party>/issued/ via the standard 5-step transmittal flow. The reviewing scaffold is deleted at issue time.<project>/.zddcIn each project, populate the document_controller and project_team role members:
In each project, populate role members:
That's it. The embedded cascade does the rest:
project_team gets read across the projectdocument_controller gets read+write project-wide, plus create authority on archive/, WORM filing rights on received/ and issued/, and subtree-admin of working/, staging/, and reviewing/project_team gets read across the project plus the in-flight ratchet (cr in working/ + reviewing/ + staging/, with rwcda inside each user's auto-own-fenced home under working/).document_controller creates party folders at archive/; when they do, the auto-own .zddc written at archive/<party>/ grants both their email AND the document_controller role rwcda — so any DC in the role has full authority at every party a peer created. Explicit rwcd grants at incoming//staging/ for the QC + transfer workflows, and WORM cr at received//issued/ for write-once filing.observer is pure read-only across the project — intended for external auditors, regulators, and read-only viewers who must not contribute content.A DC is typically also a project team member (the *@burnsmcd.com glob catches them). The embedded defaults restate document_controller: rwcda at every slot that grants project_team a narrower verb — within-level union of all matched principals gives DCs rwcda ∪ cr = rwcda, preserving full authority. Document controllers are not subtree-admins anywhere. Their power comes purely from cascade grants; admin elevation is reserved for the root admins: list (the human escape hatch).
Add a new project team member with one line; revoke by removing the line. No need to restate the cascade's grants — they're already in the embedded defaults that ship with zddc-server.
w write (overwrite existing files)c create (new files, new directories)d deletea admin (subtree-admin at this level and below)a admin (modify the .zddc ACL at this level — distinct from the root admins: list, which is the elevation-bypass sudo channel)An empty bits string ('') is an explicit deny.
That prints the embedded defaults.zddc.yaml with comments explaining every option (worm:, auto_own:, drop_target:, apps:, convert:, on_plan_review:, records:, available_tools:, default_tool:, dir_tool:, and more). Pipe it to a file and use it as the starting point for any deeper customization.
That prints the embedded defaults.zddc.yaml with comments explaining every option (worm:, auto_own:, auto_own_roles:, auto_own_fenced:, drop_target:, apps:, convert:, records:, available_tools:, default_tool:, dir_tool:, and more). Pipe it to a file and use it as the starting point for any deeper customization.