aboutsummaryrefslogtreecommitdiffstats
path: root/docs/access.md
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-07 22:35:21 +0100
committertv <tv@krebsco.de>2026-11-07 22:35:21 +0100
commit42698f42eec18d24a7378fcbe1ede73add902b71 (patch)
tree5fff4e139d56ae9e30daace4dad8c731fbffa5f1 /docs/access.md
parent64b7b2a0a94bfe92be84219c195e6335609a23e3 (diff)
access: values sealed for their readers
cs1.<age file>: a value encrypted with age to its readers' SSH keys, which no node decrypts. _continuum/access/sealed declares paths whose values must be sealed, for named readers or anyone; nodes check the recipients by age's key tags, at the API and on receipt. Auth plan M8, reworked: sealed values instead of containers, readers as users only (nodes hold no keys).
Diffstat (limited to 'docs/access.md')
-rw-r--r--docs/access.md37
1 files changed, 37 insertions, 0 deletions
diff --git a/docs/access.md b/docs/access.md
index 57462db..6f89b49 100644
--- a/docs/access.md
+++ b/docs/access.md
@@ -290,3 +290,40 @@ that checks, replacing the one there then; nothing within one; and above one, no
replacing or deleting what holds one (a map made where there is none yet is fine). So
a member writing to its document directly gets nothing there past the others: the
registry, the policy and the bans change only as a user who may change them signed.
+
+## Values sealed for their readers
+
+Anyone may read the whole document: every member holds it. A value only some may read
+is sealed for them: encrypted with age to their SSH keys (`keys`, The registry,
+above), so only they open it. Every node syncs, stores and remakes it as the string it
+is, and none decrypts it: `cs1.<the age file, base64url>` (binary, not armored).
+
+```sh
+jq -r '.keys[]' <(curl -s -u alice localhost:3000/store/_continuum/users/bob) > readers
+printf %s "$secret" | age -R readers | basenc --base64url -w0 | tr -d = |
+ sed 's/^/cs1./' | jq -R . | curl -u alice -X PUT localhost:3000/store/secrets/db -d @-
+```
+
+Any value anywhere may be sealed, for anyone. Where the swarm declares it, every value
+must be: `_continuum/access/sealed`, a list of `{"path": [...], "readers": [...]}`, `*`
+in a path for any one key, written as any of `_continuum/access` (under a strict
+policy, signed).
+
+```json
+[{"path": ["secrets", "*"], "readers": ["alice", "bob"]}, {"path": ["notes"]}]
+```
+
+Without `readers`, a value there must be sealed, for anyone; with them, sealed for
+exactly their keys, as the registry has them when it is written. A node tells without
+decrypting: an age file names each `ssh-ed25519` recipient by a tag, the first four
+bytes of the SHA-256 of its key. A write leaving a value there plain, or sealed for a
+reader's key too few or a stranger's too many, is refused (`409`), and with the policy
+every node refuses such a change on receipt (Changes judged on every node, above). A
+value signed by its user (Values signed by their users) holds the sealed value; the
+signature is over the ciphertext, so every node checks who wrote it without reading
+it.
+
+It guards against mistakes, not against a writer: one who means to leak a value has
+it. A reader removed keeps what it read: values stay sealed for the readers they were
+written for until written again, sealed for those declared then. Which values there
+are, their sizes, when and by whom they were written stay visible to every member.