Skip to content

Commits as signed envelopes on the resource: Tree::Envelopes, retention, attributable History - #1313

Merged
joepio merged 8 commits into
developfrom
cursor/commits-as-envelopes-cfac
Sep 5, 2026
Merged

Commits as signed envelopes on the resource: Tree::Envelopes, retention, attributable History#1313
joepio merged 8 commits into
developfrom
cursor/commits-as-envelopes-cfac

Conversation

@joepio

@joepio joepio commented Aug 27, 2026

Copy link
Copy Markdown
Member

Related Issues

Implements option C of planning/completed/commit-retention-floor-decision.md (amended 2026-09-05 in this PR): commits are signed envelopes, kept per resource, not a queryable event log. Follows #1370 (signed SYNC_DIFF.removeCommits). #1274 and #1254 rebase after this.

What

Commits are not resources. Ordinary content commits are not stored as Tree::Resources rows after apply; genesis, rights/parent and destroy commits still are (AuthImpact::is_critical, and any commit that brings a resource into being). Value::LoroDoc is not a KV index key. The /commits collection is not created. CommitDetail reads author and date from the resource; History has no "Show Commit" link; the Sync page no longer links commit ids. Clients no longer set previousCommit; validate_previous_commit is gone.

Signed envelopes live on the resource (Tree::Envelopes). Every signed commit's JSON-AD is written in the apply transaction, keyed pure_id ‖ createdAt ‖ signature. Not a resource, not indexed. Retention per node: --envelope-retention latest (default: the envelope that produced the current state, the audit floor) or all (every envelope: a signed audit log). The destroy envelope #1370 put on the tombstone value is now the subject's latest row here.

History is attributable. Every commit's Loro change carries a token (browser drain token; the Rust builder path and create_did now tag too). envelopes::attribute_history verifies each envelope's signature with the apply code, credits each token to one envelope (the genesis carrier only to a genesis envelope, since the inline certificate is that proof), and reports complete. Read via GET /history-attribution?subject= (read-gated), WASM ClientDb.historyAttribution, and Store.getHistoryAttribution. History's version title shows by <agent> Verified, Unverified, or by peer … Unattributed. Never a guessed signer.

Not in this PR: envelopes travelling in bulk sync and the vault pack (next, as a removeCommits-style side map), secondary indexes over envelopes, session certificates (#1310). See planning/auditability-loro-history.mdNext.

Checklist

  • Add changelog entry linking to issue, describe API changes
  • Add or update tests if needed (lib/src/envelopes.rs, server/tests/it/history_attribution.rs, browser/lib/src/history-attribution.test.ts, e2e history assertions)
  • Update docs if needed (docs/src/commits/versioning.md, docs/src/atomicserver/installation.md, planning docs, TESTING_COVERAGE.md)

@cursor cursor Bot changed the title Commits as signed envelopes: cleanup inventory Commits as signed envelopes: skip Loro index keys, optional retention Aug 27, 2026
@joepio
joepio marked this pull request as ready for review August 27, 2026 07:17
@cursor cursor Bot changed the title Commits as signed envelopes: skip Loro index keys, optional retention Commits as signed envelopes, not queryable history Aug 27, 2026
@joepio

joepio commented Sep 1, 2026

Copy link
Copy Markdown
Member Author

Decision (accepted 2026-09-01, planning/commit-retention-floor-decision.md): envelope-on-resource. This PR is on hold until Tree::Envelopes exists — the latest signed envelope must live in a side tree before stored content commits can be dropped.
Merge order: #1274#1313#1254.

joepio added a commit that referenced this pull request Sep 1, 2026
Add RFC-style decision documents, each with a "Decision needed by
maintainer" box and consequences for open PRs:

- runtime-boundary-decision.md: Rust decides, TS displays; twins only for
  pure fixtured functions; first AtomicNode slice.
- authority-unit-decision.md: drive stays the identity/replication unit;
  additive zone chain is the rights unit; what #1254 must change.
- commit-retention-floor-decision.md: envelope-on-resource floor;
  sequence #1274 -> #1313 -> #1254.
- trust-model-decision.md: the node that owns the URL is trusted with
  plaintext; anything that only stores is blind.
- schema-routes-decision.md: did:ad:frozen is the on-ramp; optional
  schema is policy; defaults fingerprint closes the repopulate gap.

Index them under "Decisions pending" in planning/README.md.

Claude-Session: https://claude.ai/code/session_019asLKBrBWY5ovyeCgtmdSd
joepio added a commit that referenced this pull request Sep 1, 2026
* docs(planning): five architecture decision documents

Add RFC-style decision documents, each with a "Decision needed by
maintainer" box and consequences for open PRs:

- runtime-boundary-decision.md: Rust decides, TS displays; twins only for
  pure fixtured functions; first AtomicNode slice.
- authority-unit-decision.md: drive stays the identity/replication unit;
  additive zone chain is the rights unit; what #1254 must change.
- commit-retention-floor-decision.md: envelope-on-resource floor;
  sequence #1274 -> #1313 -> #1254.
- trust-model-decision.md: the node that owns the URL is trusted with
  plaintext; anything that only stores is blind.
- schema-routes-decision.md: did:ad:frozen is the on-ramp; optional
  schema is policy; defaults fingerprint closes the repopulate gap.

Index them under "Decisions pending" in planning/README.md.

Claude-Session: https://claude.ai/code/session_019asLKBrBWY5ovyeCgtmdSd

* docs(planning): match the decisions index to the documents

Claude-Session: https://claude.ai/code/session_019asLKBrBWY5ovyeCgtmdSd

* docs(planning): record the five architecture decisions as accepted

All five RFCs accepted 2026-09-01. Status boxes record the outcome, the
README table is renamed to "Decisions", and encryption.md is closed to
"at-rest + vault" with an explicit reopen test.

Claude-Session: https://claude.ai/code/session_019asLKBrBWY5ovyeCgtmdSd
@gitguardian

gitguardian Bot commented Sep 5, 2026

Copy link
Copy Markdown

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

cursoragent and others added 4 commits September 5, 2026 10:33
Ordinary content commits are applied and then discarded. Genesis, rights,
parent, and destroy stay, including unflagged creations. Drop previous-commit
validation, stop indexing Loro binaries as KV keys, and stop creating the
/commits collection.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
UI reads author and dates from the resource. History no longer offers
Show Commit, the Sync page does not link commit ids, and sequential
saves no longer set previousCommit.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
Align public docs, README, changelogs, and test-coverage notes with
apply-and-discard for ordinary content commits.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
Proofs have to replicate with the resource. Preferred path is a sibling
Loro envelopes container so snapshot and SYNC_PUSH carry the signed log.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
@cursor
cursor Bot force-pushed the cursor/commits-as-envelopes-cfac branch from 3fa4336 to 27da3e1 Compare September 5, 2026 10:34
cursoragent and others added 2 commits September 5, 2026 10:45
CI failed on @tomic/lib format-check: the multi-line import of
CommitBuilder / commitIdOf / Commit is a single-line import.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
… History

Every applied signed commit leaves its JSON-AD on the resource it changed,
keyed pure_id || createdAt || signature, written in the apply transaction.
Not a resource, not indexed. Retention per node: latest (default, the
envelope that produced the current state) or all (a signed audit log),
via --envelope-retention / ATOMIC_ENVELOPE_RETENTION.

Every commit's Loro change carries a token (browser drain token; Rust
builder and create_did now tag too), so an envelope maps to the History
version it produced. envelopes::attribute_history verifies signatures
with the apply code, credits each token to one envelope (the genesis
carrier only to a genesis envelope), and reports completeness.

Read paths: GET /history-attribution (read-gated), WASM
ClientDb.historyAttribution, Store.getHistoryAttribution merging both.
History shows by <agent> Verified / Unverified / Unattributed.

The destroy envelope on the tombstone value (#1370) is now the subject's
latest row in this tree; the tombstone is a marker again.

Tests: lib envelopes (8), server it history_attribution, browser lib
history-attribution (5), e2e history assertions. Planning: decision doc
amended (no #1274 gating, retention knob), auditability doc now Building.
@joepio joepio changed the title Commits as signed envelopes, not queryable history Commits as signed envelopes on the resource: Tree::Envelopes, retention, attributable History Sep 5, 2026
…fe VersionTitle

- attribute_history read each envelope's tokens by importing its update
  into an empty doc; a browser delta has dependencies, sits pending there
  and lists no changes, so every browser edit came back with no tokens.
  Tokens are now read from the stored doc over the update's [start, end)
  range (AtomicLoroDoc::change_messages_in / update_range). Regression
  test with a real delta.
- getLoroHistory strips the drain token from Version.message for display;
  expose it as Version.token so attributionForVersion can match it.
- VersionTitle is split into flat pieces: the i18n extractor turned the
  element-spanning ternary into one placeholder message that rendered as
  [i18n-404:…] (the CI failure). Catalogues re-extracted.
@joepio
joepio merged commit 91d343c into develop Sep 5, 2026
6 checks passed
@joepio
joepio deleted the cursor/commits-as-envelopes-cfac branch September 5, 2026 12:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants