| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
Some checks failed
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 21s
wai-fleet service / does this change touch it (push) Successful in 17s
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Successful in 26s
joule-moq-room room service / joule-moq-room · does this change touch it (push) Successful in 28s
wai-app and Create / wai-app · does this change touch it (push) Successful in 22s
wai-quantum energy-landscape jobs / does this change touch it (push) Successful in 15s
joule-code conformance gate / joule-moq-relay, its own energy on a counter (macos) (push) Successful in 50s
wai-quantum editors / does this change touch it (push) Successful in 31s
wai-quantum for JavaScript / does this change touch it (push) Successful in 29s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Successful in 40s
wai-audit collector and player integration / does this change touch it (push) Successful in 1m23s
wai-quantum studio and desktop / does this change touch it (push) Successful in 30s
wai-fleet service / service · macos (push) Successful in 59s
wai-quantum for Python / does this change touch it (push) Successful in 31s
wai-quantum docs, course and locales / does this change touch it (push) Successful in 1m27s
wai-quantum views in agent hosts / does this change touch it (push) Successful in 21s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Successful in 2m4s
wai-key-proxy / does this change touch it (push) Successful in 1m55s
WAI operation catalogue / does this change touch it (push) Successful in 1m56s
wai-quantum command line / does this change touch it (push) Successful in 1m40s
wai-ledger service / does this change touch it (push) Successful in 1m50s
wai-quantum contract / does this change touch it (push) Successful in 1m41s
WAI Studio / does this change touch it (push) Successful in 12s
wai-sink-swift native sink package / does this change touch it (push) Successful in 25s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 1m43s
wai-sink-android native sink library / does this change touch it (push) Successful in 42s
joule-code conformance gate / Build + round-trip C1-C4 vectors (push) Successful in 2m45s
wai-quantum editors / editors · macos (push) Successful in 2m2s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Successful in 2m21s
wai-fleet service / service · linux (push) Successful in 3m36s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (push) Successful in 12s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Successful in 52s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Successful in 2m37s
wai-quantum for Python / python · macos (push) Successful in 2m57s
wai-ledger service / service · macos (push) Successful in 1m16s
wai-quantum editors / editors · linux (push) Successful in 3m46s
wai-audit collector and player integration / collector · linux (push) Successful in 3m39s
wai-quantum docs, course and locales / the course and the command line's languages · linux (push) Successful in 3m23s
wai-quantum for Python / python · linux (push) Successful in 4m26s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Failing after 6m38s
wai-quantum studio and desktop / desktop app · macos (push) Successful in 5m7s
wai-quantum command line / command line · macos (push) Successful in 2m33s
wai-quantum for JavaScript / javascript · linux (push) Successful in 5m35s
wai-key-proxy / proxy · linux (push) Successful in 1m12s
joule-moq-room room service / joule-moq-room, every target (push) Successful in 7m3s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 7m40s
wai-quantum energy-landscape jobs / native against WebAssembly, over many seeds (push) Successful in 7m39s
wai-ledger service / service · linux (push) Successful in 2m5s
wai-quantum contract / the policy is linked, and the deprecations page reads (push) Successful in 7s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Successful in 8m36s
wai-quantum energy-landscape jobs / the same bits · macos (push) Successful in 8m36s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Successful in 1m10s
wai-quantum views in agent hosts / views · a scripted host (push) Successful in 4m26s
wai-quantum views in agent hosts / views · the page and its engine (push) Successful in 6m33s
wai-quantum contract / API compatibility · linux (push) Successful in 4m44s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (push) Successful in 13m4s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Successful in 6m46s
wai-quantum energy-landscape jobs / reader, kernels, answers and the stated result · linux, release (push) Successful in 14m11s
wai-quantum energy-landscape jobs / the module under wasm32-wasip2 (wasmtime) (push) Successful in 14m12s
wai-quantum studio and desktop / desktop app · linux (push) Successful in 13m18s
WAI operation catalogue / the catalogue and every door · linux (push) Successful in 10m49s
WAI Studio / Studio in Chromium and Firefox · macos (push) Successful in 11m36s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Successful in 12m10s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Successful in 16m36s
wai-quantum contract / schemas and the committed tool lists · linux (push) Successful in 9m58s
wai-quantum studio and desktop / studio · browser (push) Successful in 16m35s
WAI Studio / Studio in WebKit · linux (push) Successful in 12m22s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (push) Successful in 22m0s
wai release binaries build on Windows / the seven release binaries · windows (push) Successful in 24m31s
joule-code conformance gate / joule-moq-ingest, every target (push) Successful in 28m51s
joule-code conformance gate / joule-moq-relay, every target (push) Successful in 28m59s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Successful in 29m41s
wai-quantum studio and desktop / desktop app · windows (push) Successful in 11m58s
wai-quantum command line / command line · linux (push) Successful in 48m17s
Folds, each the program it folds run as itself, so what it prints and its exit status are the program's for the same arguments: wai audit report|check|verify (audit.create, audit.check, audit.verify) is wai-audit, --owner its WAI_AUDIT_OWNER; wai serve <role> [--config F] [--check] (service.start) starts wai-<role>: relay, room, ingest and key-release from a file, audit, fleet, ledger and quantum-service from their environment; wai quantum run|plan|lint|verify|methods (quantum.run, .inspect, .check, .verify, .list) is wai-quantum with that verb, and no other verb of it is reached. A program is its flag, WAI_<NAME>_BIN, beside wai, or on PATH; one not there is exit 5, program-not-found. report is not an N3 verb, so wai audit report is the operation audit.create. --check on the daemons: wai-relay, wai-room (its keys too) and wai-ingest --config F --check, and wai-key-release serve F --check, read the configuration as each starts from it and start nothing, exit 1 with the reason when it does not read. The proxy is installed as wai-key-release; wai-key-proxy is the same program under its earlier name. wai cmcd encode|decode|verify is wai_telemetry's tool compiled in (cmcd.rs, one module behind both; telemetry joins full). wai receipt create <file>... --from <input>... [--key] signs the operation receipt a toolchain write leaves, for files made elsewhere. wai verify reads every record by its kind: by name, and a JSON record by what it holds (a delivery receipt or claim as receipt_verify answers it, a room session, an audit bundle as wai-audit verify does with --owner/--channel, a sealed playlist by its mr/<blake3>.json as check-playlist does); a file of no kind is exit 2, unknown-record. wai mcp --toolset quantum starts wai-quantum mcp and answers each quantum_* tool with that server's own answer and schemas. wai media score build|render alias wai score. The quantum operations' SDK names are registered labels. Tests, each fold against what it folds: cli_folds (cmcd over the whole corpus, receipt create against wrap's receipt, verify on the claim, JWP, room and audit corpora and a sealed playlist, audit|serve|quantum against stand-ins); wai-audit tests/fold.rs, wai-quantum-cli tests/wai_fold.rs and joule-moq-room tests/cli_serve.rs against the real programs; cli_mcp's quantum toolset; the daemons' --check (config_check.rs, check.rs). ops.json, mcp.json, the registry export and Studio's actions regenerated. Co-Authored-By: Claude Opus 5.5 <[email protected]> |
||
| .. | ||
| src | ||
| tests | ||
| Cargo.lock | ||
| Cargo.toml | ||
| README.md | ||
| smoke.sh | ||
wai-key-proxy
A signing proxy in front of any licence server, and the key server it hands clear keys to.
It is the service of extensions/key-release.md.
For every licence request it answers, the proxy signs a receipt: a release or a refusal. The decision is computed from signed inputs, so anyone holding the receipts recomputes it: the viewer's entitlement (signed by the operator's entitlement authority and bound to a key the viewer's device holds), the device's signature over the request, the content's commitment list (signed by whoever holds the content keys, and derived from the CPIX document the keys came in), the proxy's clock and the receipts before it. A licensor checks that keys went only to entitled, unexpired sessions from the log, not from a statement.
The proxy holds no content key:
- each
key_commitit signs is copied from the key holder's commitment list; - a content protection system's licence carries the key encrypted for the client's decryption module, and the proxy forwards it unchanged;
- a key the client gets in the clear (an HLS key file, a Clear Key licence) is never relayed: the proxy answers 307 with a release ticket, and the key server, run by the key holder, redeems the ticket once.
A refused request never reaches the licence server.
Run it
cargo build --release
B=target/release/wai-key-proxy
$B keygen holder.key # the key holder's signing key; prints its public key
$B keygen authority.key # the entitlement authority's
$B keygen proxy.key # the proxy's
# A commitment list from the CPIX document the packager and the key server exchanged:
$B list title.cpix.xml svod:premium holder.key https://packager.example title.wkl1
$B serve proxy.json # the proxy
$B keyserver keys.json # the key server, for HLS key files and Clear Key
proxy.json:
{
"listen": "127.0.0.1:9710",
"admin_listen": "127.0.0.1:9711",
"signer_key": "/var/lib/wai-key-proxy/proxy.key",
"signer_id": "https://keys.operator.example",
"authorities": ["<entitlement authority public key, hex>"],
"lists": ["/var/lib/wai-key-proxy/lists/title.wkl1"],
"routes": [
{"name": "title.a", "content_id": "title-0001", "delivery": "drm",
"system_id": "<the content protection system's DASH-IF system ID>",
"upstream": "http://127.0.0.1:9720/licence",
"upstream_headers": {"<a header the licence server expects>": "<its value>"}},
{"name": "title.hls", "content_id": "title-0001", "delivery": "hls-key-uri",
"key_url": "https://keys.operator.example/v1/key"}
],
"log": "/var/lib/wai-key-proxy/receipts.jsonl",
"ticket_ttl_s": 60,
"upstream_timeout_ms": 10000,
"max_body": 65536
}
keys.json:
{
"listen": "127.0.0.1:9712",
"cpix": ["/var/lib/wai-key-server/title.cpix.xml"],
"proxies": ["<proxy public key, hex>"],
"redeemed": "/var/lib/wai-key-server/redeemed.txt"
}
The proxy refuses to start on a route whose delivery cannot carry a listed key's scheme (§3.3), on a list whose signature does not verify, on a log written by another proxy key, and on a log another process holds.
A licence server reached over TLS is reached through a local forwarder that originates TLS
(upstream is http://): a reverse proxy listening on 127.0.0.1 whose upstream is the
licence server's https:// address. The proxy carries no TLS stack of its own; the reverse
proxy in front of it terminates the client's TLS.
Requests
A client sends the licence challenge (or nothing, for an HLS key file) to
/v1/licence/<route> with the header fields of §6.2: WAI-Key-Ids, WAI-Key-Kind,
WAI-Key-Time, WAI-Key-Entitlement, WAI-Key-Proof and, for a renewal, WAI-Key-Renews.
wai-key-proxy sign-request writes them for a test or a tool; a player computes the proof with
the Web Cryptography API (Ed25519 and SHA-256).
| answer | when |
|---|---|
| 200 (or the licence server's status), the licence | a content protection system's licence was granted and forwarded |
307 to the key server with wai-ticket |
a clear key was granted |
403 and the receipt, application/json |
refused; nothing was forwarded |
| 400, 404 | the request names no key or no route; it has no receipt |
Every decided request names its receipt in WAI-Key-Receipt, readable at
/v1/receipts/<identity>. /v1/lists and /v1/health describe the proxy; the whole log is on
admin_listen at /v1/log, which is not public.
GET /metrics answers the proxy's counters in the text exposition format (../wai-metrics):
requests by route, method and status (wai_key_proxy_requests_total), receipts made durable by
decision and delivery (wai_key_proxy_receipts_total), granted requests forwarded by the class of
the licence server's answer (wai_key_proxy_upstream_total), the receipts in the log, the requests
at the licence server now and the proofs of granted requests whose receipts are not yet in the log.
No route name, content, key, device, entitlement or client is a label. It is the operator's, and
answers on admin_listen when the proxy has one, the public listener then answering 404. A proxy
with no admin_listen answers it on listen, where a request that came through the reverse proxy
in front, one carrying X-Forwarded-For or another of the fields a front sets, is answered 404,
and a reverse proxy that sets none of them is configured not to pass the path on. admin_listen is
not public, and answers whatever fields a request carries.
Check a log
$B verify receipts.jsonl <authorities,hex> <proxies,hex> title.wkl1 ...
runs the reference verifier of §10 over the log. The independent verifier of the conformance
corpus is live-conformance/key_release_verify.py.
Tests
cargo test runs the proxy, the key server and a fixture licence server on loopback:
releases, refusals that never reach the licence server, a replay sent while the first request
is still being forwarded, offline licences and renewals, a licence server's refusal, tickets
redeemed once, no key byte in the log, a restart that keeps the replay and renewal history,
the whole log under the reference verifier, and GET /metrics read strictly as the text format,
counting those receipts and naming none of their routes, contents, keys or devices, on the
listener that is not public when there is one. bash smoke.sh runs the release binary as an
operator would.