Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
d5c9701
feat(protected-content): add canonical custody envelope contracts
andersalm Aug 14, 2026
f15854b
feat(protected-content): seal custody shares for verified releases
andersalm Aug 14, 2026
401d6d0
feat(protected-content): reconstruct threshold content keys
andersalm Aug 14, 2026
3e3caf2
fix(protected-content): reject invalid X25519 contract keys
andersalm Aug 14, 2026
088e4f7
fix(protected-content): bind exact threshold reconstruction
andersalm Aug 14, 2026
927dfd6
feat(protected-content): define policy and recipient authority
andersalm Aug 14, 2026
58c00c7
feat(protected-content): bind custody epochs and release operations
andersalm Aug 14, 2026
1889994
docs(protected-content): record operational contract boundaries
andersalm Aug 14, 2026
2c2e0dc
feat(protected-content): gate node release on durable replay claims
andersalm Aug 14, 2026
854842b
docs(protected-content): record custody operation boundary
andersalm Aug 14, 2026
3b07fd4
feat(protected-content): replay exact custody results
andersalm Aug 15, 2026
4bbadb1
docs(protected-content): define canonical custody architecture
andersalm Aug 17, 2026
bd2d799
docs(protected-content): record published custody truth
andersalm Aug 17, 2026
29879b2
test(protected-content): label provisional provider retirement guard
andersalm Aug 17, 2026
305354e
Merge branch 'feat/protected-content-contracts' into feat/protected-c…
irzhywau Aug 27, 2026
2f844ce
fix(protected-content): verify statement signatures strictly
irzhywau Aug 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,23 +135,35 @@ The contract and its limits live in

### 3. Build Runtime-mediated protected content

Protected content uses the same effect path as ordinary content:
The target protected-content design uses the same effect path as ordinary
content:

`viewer -> Runtime capability -> protected-content coordinator -> providers`
`viewer -> Runtime -> rights provider -> custody providers -> decrypt provider`

The coordinator resolves the content object, checks availability, verifies
rights, requests key release, creates a decrypt or render session, and records
the result. Each dependency has a typed contract and fails closed. Viewers
receive scoped output or a scoped session, not content keys, chain RPC, wallet
authority, storage APIs, or provider credentials.
Runtime will resolve the content object, check availability, verify rights,
request recipient-encrypted custody contributions, create a scoped decrypt or
render session, and record the result. Each dependency will have a typed
contract and fail closed. Viewers will receive scoped output or a scoped
session, not content keys, custody shares, chain RPC, Wallet authority, storage
APIs, provider routes, network locations, or credentials.

The dependency order for this work is strict:

1. review the canonical v1 contract as source only;
2. review custody, recipient-encryption, replay, and policy design;
3. replace the provisional DTO/provider surface atomically and wire
Runtime/Wallet/provider integration to the reviewed contract; and
4. prove the installed end-to-end open/decrypt/render path.
A. review the canonical v1 source-only contract and operational contract
surface, including the exact EVM `has_access_by_content_id`
policy/evidence contract, recipient-key authorization, immutable custody
epochs, and the authenticated replay-pending Runtime-to-release-node
envelope;
B. build source-only custody-node operations and durable node state on top of
that contract. The published custody branch covers the node-local durable
dual-key claim, claim-gated release, exact encrypted-result persistence, and
exact retry replay. Remaining work is issuer lifecycle, node admission,
rotation, recovery, operational review, and recovery from a durable claim
that has no durable result;
C. replace the provisional DTO/provider surface atomically and wire
Runtime/Wallet/provider integration to the reviewed contract, with one
source allow flow and one source deny flow; and
D. prove the installed end-to-end open/decrypt/render path.

Carrier remains transport only throughout that sequence. It carries
Runtime-selected traffic, but it does not define rights authority, custody
Expand Down
199 changes: 133 additions & 66 deletions TASKS.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion capsules/decrypt-provider/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion capsules/drm-provider/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion capsules/key-provider/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion capsules/rights-provider/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 11 additions & 7 deletions docs/CARRIER.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,19 @@ Carrier should make content exchange feel like one SmartWeb, but it should not
own all storage policy. The content provider owns publish/fetch/status/repair
semantics and availability receipts. Carrier owns secure peer discovery,
messaging, relay, and peer/object transport. IPLD gives the traversable CID graph
shape. Rights/decryption remain in the access provider and dDRM layer.
shape. In the target protected-content cutover, rights, custody, and decryption
remain in Runtime-selected providers.

Provider-to-provider Carrier invocation follows the same boundary. Runtime adds
an `elastos.provider.invocation/v1` envelope, selects `carrier-provider-plane`,
and sends a generic Carrier `provider_invoke` message only between service
providers such as `content`, `availability`, `rights`, `key`, `decrypt`, and
`drm`. Raw connect tickets stay inside Runtime transport state and are not
returned in app-visible receipts. Raw backend providers such as `ipfs` or
`localhost` remain local implementation details, not remote Carrier authorities.
an `elastos.provider.invocation/v1` envelope and selects
`carrier-provider-plane`. The current Carrier provider target allowlist includes
`content`, `availability`, and the provisional `rights`, `key`, `decrypt`, and
`drm` labels. It does not yet include a custody route. The target cutover keeps
the same transport envelope but replaces the provisional protected-content
surface with Runtime-selected `rights`, `custody`, and `decrypt` providers. Raw
connect tickets stay inside Runtime transport state and are not returned in
app-visible receipts. Raw backend providers such as `ipfs` or `localhost` remain
local implementation details, not remote Carrier authorities.
If the provider transfer is `stream`, the Carrier side also validates the
target-visible `elastos.provider.stream/v1` base64-chunk contract before
dispatching the request. Carrier availability fetches use that path for remote
Expand Down
59 changes: 34 additions & 25 deletions docs/CONTENT_AVAILABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ IPLD can identify and traverse a CID graph. It does not decide:
- who pays volunteer or professional storage providers
- who may decrypt sealed content

Those are Runtime, Carrier, availability, dDRM, key-provider, decrypt-provider,
and blockchain responsibilities.
Those are Runtime, Carrier, availability, rights, custody, decrypt, and Chain
provider responsibilities.

## Capsule-Facing Contract

Expand Down Expand Up @@ -596,9 +596,13 @@ invocation envelope for IPFS and availability effects. The envelope validates
source, target, operation, transfer class, byte range, and progress receipt
metadata. The provider plane now has explicit local and Carrier transports:
Carrier `provider_invoke` runs over the Carrier ALPN with the same
`elastos.provider.invocation/v1` envelope, hides raw connect tickets from
receipts, and only dispatches to service providers (`content`, `availability`,
`rights`, `key`, `decrypt`, `drm`) rather than raw backends.
`elastos.provider.invocation/v1` envelope and hides raw connect tickets from
receipts. Its current provider target allowlist includes `content`,
`availability`, and the provisional `rights`, `key`, `decrypt`, and `drm`
labels. It does not yet include a custody route. The target cutover will retain
the envelope while replacing the provisional protected-content labels with
Runtime-selected `rights`, `custody`, and `decrypt` providers rather than raw
backends.
`ProviderTransfer::Stream` now carries validated
`elastos.provider.stream/v1` base64 chunks with range/progress receipts, and
`content-provider` fetch opens that stream envelope as a Runtime-owned session
Expand Down Expand Up @@ -640,24 +644,28 @@ dashboard rows.
Runtime-owned fields; source providers cannot predeclare or spoof them in the
target request.

## dDRM and Protected Content
## Protected Content

Availability stores bytes. Rights decide who may use them.

Protected content should be encrypted before it is published. IPFS, Elacity,
supernodes, and volunteers can safely store encrypted blocks. The dDRM or
ElastOS-native rights provider verifies ownership, subscription, access token,
or license state. The `drm-provider` owns the protected-content open contract
and should delegate to rights, key, and decrypt providers. The key provider
releases a short-lived decryption capability only after Runtime authority and
rights checks pass.
supernodes, and volunteers can store encrypted blocks. In the intended open
path, Runtime will own the operation. It will verify the authenticated request,
ask `rights-provider` for typed policy evidence, select custody providers for
recipient-encrypted contributions, and create a scoped `decrypt-provider`
session. Carrier will transport only Runtime-selected endpoint traffic.

The current Library path still uses the provisional
`drm-provider -> rights-provider -> key-provider -> decrypt-provider` sequence.
Those providers remain fail closed without configured backends. They do not
implement or verify the intended custody path.

That keeps the core invariant simple:

```text
public CID can expose encrypted bytes
valid rights holder gets decrypt capability
unauthorized opener gets no key
valid rights holder gets scoped output
unauthorized opener gets no decrypt session
```

## Default SmartWeb Sync
Expand Down Expand Up @@ -745,21 +753,22 @@ verifies/summarizes remote content availability receipts when present, and other
`carrier_announced` or `repair_needed`; if Carrier is unavailable, publish
remains honestly local or `repair_needed`. Public gateway
installer publishing now also uses `elastos://content/publish` instead of
direct IPFS. The
protected-content
foundation now adds shared sealed-object schemas, a fail-closed `drm-provider`
for `elastos://drm/open`, a fail-closed `rights-provider` for typed access
questions, a fail-closed `key-provider` for key release, a fail-closed
`decrypt-provider` for decrypt/render sessions, a canonical
`drm-provider.status.required_sequence` for protected-content opens, a
machine-readable fail-closed `open` response that repeats that sequence,
Runtime-owned receipt/audit steps in that sequence, and typed `chain-provider`
rights reads. The content provider now validates
direct IPFS. The protected-content source tree now adds canonical v1 contracts
and custody helpers for typed rights evidence, signed recipient authorization,
immutable custody epochs, recipient-encrypted node contributions,
exact-threshold reconstruction, node-local durable claims, and exact encrypted
result replay. It is source-only and does not provide Runtime orchestration,
installed custody nodes, decryption, or playback. Released 0.6 also retains the
provisional fail-closed DRM, rights, key, and decrypt capsules as a retirement
surface; they do not prove the canonical v1 path. The content provider now validates
`sealed` object publishes against the sealed descriptor, required graph links,
and protected-content algorithm allowlists. The
remaining work is:

- Wire sealed-object publish/open to rights, PQ-hybrid key release, and decrypt/render providers without exposing raw CEKs or backend SDKs to app capsules.
- Replace the provisional protected-content DTO/provider surface atomically with
Runtime-owned rights, custody, and decrypt orchestration. Do not expose raw
CEKs, custody shares, provider routes, network locations, or backend SDKs to
app capsules.
- Move remaining release artifact uploads off explicit operator IPFS tooling when the release pipeline can use the same content-provider contract without losing install compatibility or build/release proof.
- MicroVM materialization paths should consult availability state and repair/fetch through explicit operator/provider-plane tooling.
- Promote the first Carrier remote proof path into production-grade storage policy: production independent provider-network quota-ledger federation beyond the configured bounded endpoint quorum, repair-fleet worker attestations/SLA/settlement beyond the configured external dispatch endpoint quorum, live market pricing/escrow/settlement beyond the configured storage-market endpoint-quorum admission gate and current storage-settlement policy-status receipt, actual federated network abuse throttles/banlists/abuse ledgers beyond the configured bounded abuse-control endpoint quorum, richer remote storage receipt policy, production peer reputation trust policy/third-party attestations/revocation beyond the configured Carrier peer-attestation endpoint quorum, and live federated dashboard/UI/peer-health subscriptions beyond the current provider-local dashboard plus configured alert-exchange endpoint.
Expand Down
111 changes: 26 additions & 85 deletions docs/DECRYPT_PROVIDER.md
Original file line number Diff line number Diff line change
@@ -1,101 +1,42 @@
# Decrypt Provider

`decrypt-provider` is the protected-content decrypt/render boundary. It
validates scoped decrypt-session requests and fails closed until a real
decrypt/render backend is configured.
`decrypt-provider` will be the canonical protected-content decrypt and render
boundary. Runtime will bind one scoped session to the exact authenticated
Profile, Wallet-approved action, object, rights evidence, recipient
authorization, custody epoch, expiry, and provider identity.

The contract is:
The intended path is:

`capsule -> runtime capability -> elastos://decrypt/* -> decrypt-provider -> decrypt/render backend`
`Runtime coordinator -> decrypt-provider -> decrypt/render backend`

This page describes the older provisional provider surface. It does not
describe or prove the canonical source-only v1 contract in
[`PROTECTED_CONTENT_CONTRACTS_V1.md`](PROTECTED_CONTENT_CONTRACTS_V1.md). Future
integration must replace the provisional surface atomically, with no parallel
decoder, fallback, or migration path.
Custody providers return recipient-encrypted contributions. Runtime may relay
those opaque contributions or other sealed material, but it cannot open them.
The decrypt boundary is the only component that may reconstruct and briefly
hold the live CEK. It returns scoped output or an opaque session handle and
zeroizes the CEK when the session ends. It must not make outbound calls to gain
rights, custody, Wallet, Chain, storage, or network authority.

Apps and viewers must not receive raw CEKs, broad plaintext authority,
filesystem authority, key-backend SDKs, KMS credentials, chain RPC, wallet RPC,
or provider credentials. They receive scoped rendered output, streams, or
working copies after Runtime, rights, and key-release checks succeed.
Capsules receive no raw CEK, raw plaintext authority, custody shares, provider
routes, endpoint DIDs, network locations, credentials, filesystem authority,
Wallet RPC, Chain RPC, or backend SDK.

## Operations
## Current source state

- `status` -> `elastos://decrypt/meta/status`
- `open_session` -> `elastos://decrypt/session/open`
- `render` -> `elastos://decrypt/render`
The canonical Runtime-to-decrypt handoff is future integration work. The
source-only contracts and custody crate do not claim decryption, rendering,
installation, or playback.

Unsupported operations fail closed and do not create broad provider wildcards.

## Request Shape

The current provisional `elastos_common::protected_content::DecryptSessionRequestV1`
binds:

- request ID
- principal ID
- session ID
- object CID
- requested action
- viewer interface
- typed `elastos.release.receipt/v1` from `key-provider`, bound to the same
principal/session/object/action
- output kind
- reason
- expiry

The current provider accepts only documented protected-content actions and
outputs: `rendered`, `stream`, and `working_copy`.

## Key Material Rail

The current provisional `elastos_common::protected_content::ReleaseReceiptV1`
is an authorization receipt, not key material. It proves that the key-provider
accepted the rights-bound release request for the same
principal/session/object/action, but it does not contain a CEK.

When the real decrypt backend is wired, the recommended normal path is:

- `decrypt-provider` creates a per-session one-time public key for the decrypt
sandbox.
- `key-provider` or the dKMS release backend seals decrypt material to that
public key.
- `DecryptSessionRequestV1` is extended with a sealed decrypt material envelope,
bound to the same principal/session/object/action and release receipt.
- The decrypt sandbox unwraps inside the boundary, decrypts/renders, zeroizes
the live CEK, and returns only scoped output.
- `decrypt-provider` must not be granted outbound key-fetch authority as the
normal path.

This preserves the provider-to-provider chain without letting the component that
briefly sees the live CEK call out to other providers or raw backends.

The alternatives are intentionally narrower:

- outbound key-fetch from `decrypt-provider` is only acceptable for an explicit
adapter with a scoped, audited capability, not as the default rail.
- a combined key/decrypt provider is acceptable for tests but not for the target
trust boundary because it merges dKMS authority with decrypt/render authority.
- Lit/Chipotle-style CEK envelopes are compatibility inputs only. The decrypt
contract must validate a backend-neutral sealed material envelope so an
ElastOS-native dKMS backend can replace vendor key release.

## Current State

The provider is intentionally not configured for real decrypt/render work yet.
It only proves the Runtime/provider boundary:

- request validation is typed
- object IDs are treated as opaque identifiers
- raw CEK and raw plaintext paths are absent
- `open_session` and `render` return `not_configured`
- the sealed decrypt material envelope is not implemented yet
Released 0.6 contains an older provisional `decrypt-provider` capsule. It uses
the old `elastos_common::protected_content` DTO, validates requests, and returns
`not_configured`. It remains only as a fail-closed retirement surface and must
be replaced atomically. It does not verify the canonical v1 path.

## Verification

Provisional retirement guard only:

```bash
cargo test --manifest-path capsules/decrypt-provider/Cargo.toml
cargo clippy --manifest-path capsules/decrypt-provider/Cargo.toml -- -D warnings
cargo test -p elastos-server --manifest-path elastos/Cargo.toml provider_resource
scripts/check-wci-alignment.sh
bash scripts/protected-content-provider-contract-smoke.sh
```
Loading
Loading