Skip to content

feat(protected-content): atomic cutover to the Runtime-owned authority (ELACITY-2299) - #59

Closed
irzhywau wants to merge 2 commits into
feat/protected-content-installed-e2e-prooffrom
feat/protected-content-atomic-cutover
Closed

irzhywau wants to merge 2 commits into
feat/protected-content-installed-e2e-prooffrom
feat/protected-content-atomic-cutover

Conversation

@irzhywau

@irzhywau irzhywau commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Closes ELACITY-2299. Stacked on #60 (feat/protected-content-installed-e2e-proof) — merge after #60.

Stack: #51 promotes upstream/0.7.1-dev to main; #60 lands the installed e2e proof on upstream/0.7.1-dev; this PR sits on top of #60. #52 is already merged, and #57 was replaced by #60 after both branches were rebased onto the post-#58 release line.

What this does

Makes the Runtime-owned protect/media/custody/decrypt plane with Chain rights evidence the only protected-content authority, and removes the provisional one in the same slice. No fallback, no dual route, no compatibility decoder.

The canonical path was already the installed path (proven by ELACITY-2298), so this is a removal plus relabeling rather than a behavioral switch.

Removed

  • the drm-provider, rights-provider, key-provider and decrypt-provider capsules, and their startup spawn/registration in server_infra
  • the elastos_common::protected_content DTO module
  • the elastos://drm|rights|key|decrypt capability mappings — those schemes now fail closed as unsupported
  • those four names from the provider registry's RESERVED_SUB_NAMES allowlist, so a later provider cannot re-register the routes and quietly recreate a second authority
  • the Library share provider chain and its readiness projections
  • the content plane's sealed object kind, whose descriptor validation could not outlive SealedObjectV1. Nothing published one — the canonical mint publishes encrypted payloads through the ordinary content path
  • the four providers from components.json, the publish/build sets and the capsule catalog
  • scripts/protected-content-provider-contract-smoke.sh, which asserted that capsules that no longer exist stay fail-closed

The gates now assert the inverse

setup.rs, publish.rs, check-wci-alignment.sh and the installed static audit fail if any retired name reappears in a capsule tree, components.json, an install profile, or the capability mapping. The entropy check's decode-time invariant moved from the deleted DTO to the canonical provider contracts, which carry the same deny_unknown_fields guarantee.

Product-visible

Library share of plain published content is unchanged. Its projections and the share dialog now say protected content is published through Runtime custody publish, instead of offering a permanently disabled "Protected sharing" option and reporting a provider chain that could never be configured.

The custody state root keeps its on-disk path protected-content/custody-provider/inactive — provisioned hosts and the container harness already hold state there, so the directory name is frozen and documented as historical.

Second commit: dead code

c4ba56ab removes 42 public items that had exactly one occurrence in the repository — their own definition — across all file types, and were executed by no test. rustc cannot flag these: dead_code assumes a public item has callers outside the crate, and in this closed workspace none of them do. Kept separate so it can be dropped independently of the cutover.

Verification

Source — cargo fmt --check; cargo clippy --workspace --all-targets -- -D warnings clean; just test 3751 passed / 0 failed; git diff --check; grep sweep for retired names/routes empty; all source gates pass, including check-wci-alignment.sh, both entropy checks, and system-map-check.mjs (15 diagrams — a new protected-content open dynamic view, with its change-gate record in the map README).

Installed, post-cutover — installed from c4ba56ab (receipt: clean tree, runtime parity true); the installed gateway startup log references zero retired providers and no stale provider binaries remain in bin/; 3 custody nodes up with Carrier bound and the chain plane accepting the evidence RPCs; preflight_ok=true; and the full journey against an Anvil fork of Base:

finalize overall_ok=true, all 12 phase verdicts true — provision, preflight, wallet_setup, mint, availability, buy, open, drill_custody, drill_replica, negative, restart, cleanup.

That includes serving on 2-of-3 with a custody node stopped and 3-of-3 after its restart, degraded buy/open refused then healed by the repair worker, below-quorum/denial/foreign/stale/tamper all rejected, and a mint surviving SIGKILL between wallet approval and confirmation with no duplicate transaction.

Note for reviewers

The nested managed-runtime data dir carries its own protected-content/chain-provider.json, and its protected-content root was mode 0755 while the client's is 0700. generate-chain-config correctly refuses a non-owner-only root, but the loader read that world-readable config without complaint. That asymmetry predates this change and looks worth its own ticket.

Make the Runtime-owned protect/media/custody/decrypt plane with Chain
rights evidence the only protected-content authority, and remove the
provisional one in the same slice. No fallback, dual route, or
compatibility decoder survives.

Removed:
- the drm-provider, rights-provider, key-provider and decrypt-provider
  capsules, and their startup spawn/registration in server_infra;
- the elastos_common::protected_content DTO module;
- the elastos://drm|rights|key|decrypt capability mappings, which now
  fail closed as unsupported schemes;
- those four names from the provider registry's RESERVED_SUB_NAMES
  allowlist, so a later provider cannot re-register the routes and
  quietly recreate a second authority;
- the Library share provider chain and its readiness projections;
- the content plane's "sealed" object kind, whose descriptor validation
  could not outlive SealedObjectV1. Nothing published one: the canonical
  mint publishes encrypted payloads through the ordinary content path;
- the four providers from components.json, the publish/build sets and
  the capsule catalog;
- scripts/protected-content-provider-contract-smoke.sh, which asserted
  that capsules that no longer exist stay fail-closed.

The gates now assert the inverse of what they asserted before. setup.rs,
publish.rs, check-wci-alignment.sh and the installed static audit fail if
any retired name reappears in a capsule tree, components.json, an install
profile, or the capability mapping. The entropy check's decode-time
invariant moves from the deleted DTO to the canonical provider contracts,
which carry the same deny_unknown_fields guarantee.

Library share of plain published content is unchanged. Its projections
and the share dialog now say protected content is published through
Runtime custody publish, instead of offering a permanently disabled
option and reporting a provider chain that could never be configured.

The custody state root keeps its on-disk path
protected-content/custody-provider/inactive: provisioned hosts and the
container harness already hold state there, so the directory name is
frozen and documented as historical.

Docs, state.md, TASKS.md gate 9, the changelog and the system map are
aligned in this slice. The map gains a protected-content open dynamic
view drawn as installed product structure, with its change-gate record.
Every item removed here had exactly one occurrence in the repository —
its own definition — across all file types, and none was executed by any
test. rustc cannot flag these: the dead_code lint assumes a public item
has callers outside the crate, and in this closed workspace none of them
do.

Notable removals, all dead rather than merely untested:
- three set_*_for_testing hooks on RunningVm that no test calls;
- an auth challenge store (store/load/consume_challenge) and
  rotate_session_grant, superseded by the current session path;
- start_tls_proxy, download_verified, publish_directory_via_provider and
  start_local_principal_runtime_session_with_transport, entry points
  nothing reaches;
- three Documents request structs with no route behind them;
- assorted unused builders and accessors (with_key, with_gateway,
  with_cache_limit, from_hex, new_unchecked, ok_with_data, after_mins).

is_plaintext_root looked unreferenced by the same measure but is used by
elastos/capsules/localhost-provider, so it stays. Imports orphaned by the
removals are dropped in the same change.

The suite is unchanged at 3751 passing, which is the expected result when
what is deleted was never executed.
@irzhywau

irzhywau commented Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #203, rebuilt on the current develop as the first of a new stack: #203 → #204 → #205 → #206.

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