docs: extend interface spec with delegation permissions field#292
docs: extend interface spec with delegation permissions field#292aterga wants to merge 5 commits into
Conversation
Specify the optional permissions field of request delegations drafted in dfinity/ic#10449: - https-interface.md: document the permissions field of the delegation map ("queries" restricts the delegation to query calls and read_state requests, "all" is the same as omitting the field, any other value makes the delegation invalid for all kinds of requests) and add permissions to the string-typed fields in the representation- independent hashing section. - abstract-behavior.md: extend SignedDelegation with permissions : Text | Unrestricted and amend verify_envelope / verify_delegations so that unsupported values fail verification for all requests and update calls fail if any delegation in the chain is restricted to queries. - changelog.md: add a 0.63.0 entry for the feature. https://claude.ai/code/session_01WBqBka57Q7xYi4btZYfPqT
|
🤖 Here's your preview: https://k7oic-piaaa-aaaam-ai7uq-cai.icp0.io |
There was a problem hiding this comment.
Pull request overview
This PR updates the Internet Computer (IC) interface specification to describe a new optional permissions field on request delegations, aligning the spec and formal model with the draft replica implementation semantics (including “whole-chain” restrictions).
Changes:
- Documented the optional
permissionsdelegation field and its semantics in the HTTPS interface spec (including hashing coverage for signatures). - Updated the formal model (
abstract-behavior.md) to modelpermissionsand enforce it inverify_delegations/verify_envelope. - Added a changelog entry for the new field (currently marked as needing release/version/date confirmation).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| docs/references/ic-interface-spec/https-interface.md | Adds permissions to delegation documentation and includes it in hashing examples. |
| docs/references/ic-interface-spec/abstract-behavior.md | Extends the formal SignedDelegation model and updates verification predicates to enforce permissions semantics. |
| docs/references/ic-interface-spec/changelog.md | Adds a new changelog entry describing the permissions delegation field. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The draft status was only visible in an HTML comment, so the rendered changelog presented a provisional version and date as a finalized release. Mark the entry visibly as unreleased and drop the date until the feature ships. https://claude.ai/code/session_01WBqBka57Q7xYi4btZYfPqT
|
Feedback addressed:
Generated by Claude Code |
|
We should also adapt |
Mirror the new optional permissions field of request delegations in the
CBOR schema. Enumerate the supported values ("queries" / "all") to match
the spec, consistent with how request_type and status enumerate literals.
https://claude.ai/code/session_01WBqBka57Q7xYi4btZYfPqT
Per review on abstract-behavior.md, type the permissions field as the literal enumeration "queries" | "all" | Unrestricted, consistent with the requests.cddl encoding and with how request_type and status enumerate literals. The type now constrains the supported values, so the explicit membership check in verify_delegations is redundant and removed; the update-call restriction (queries-restricted delegations reject update calls) stays. https://claude.ai/code/session_01WBqBka57Q7xYi4btZYfPqT
…odel Apply review suggestion: use capitalized variant names Queries | All | Unrestricted for the permissions field, matching the formal model's convention for enumerations (e.g. RequestStatus) rather than quoted string literals. Update the verify_envelope comparison and the predicate's preamble to reference the Queries variant, noting it is encoded as the text "queries". The requests.cddl keeps the quoted "queries" / "all" encoding. https://claude.ai/code/session_01WBqBka57Q7xYi4btZYfPqT
Per the settled interface-spec model (dfinity/developer-docs#292, which defines the field as the union `Queries | All | Unrestricted`), replace the `Option<String>` `permissions` field with a typed `Option<DelegationPermissions>` enum whose `Queries`/`All` variants are encoded on the wire (and in the representation-independent hash) as the text `"queries"`/"all"". Consequences: - Unsupported values are now rejected when the request is decoded (the field is a closed enum), so the runtime AuthenticationError::UnsupportedDelegationPermissions check in validate_delegations is gone, along with the error variant in both the validator and ingress-message crates. - The "unsupported value rejected" coverage moves to a CBOR-decoding test in ic-types (delegation_permissions_rejects_unsupported_value), which also covers case/whitespace variants of the supported values. - The validator/e2e/system tests that previously constructed invalid string permissions are removed or switched to the typed enum. https://claude.ai/code/session_01BQNgPJKgxWohrJ6owwJBzz
…ns (dfinity#10449) # Queries-only permissions on delegations **This is the consolidated read-only-sessions feature** (the previously stacked alternatives dfinity#10447/dfinity#10452, which carried the restriction in certified `sender_info` attributes, were closed in favor of this more lightweight approach). ## What it does Request delegations gain an optional `permissions` field, mirroring the existing `targets` field. It is modeled as a typed enum `DelegationPermissions` whose variants are encoded on the wire (and in the representation-independent hash) as text: * `permissions` **absent** or **`"all"`** (`DelegationPermissions::All`) → the sender can execute all functions — queries, replicated queries, and updates (today's semantics; byte-for-byte identical hash for existing delegations without the field). * **`"queries"`** (`DelegationPermissions::Queries`) → the sender can only execute queries: requests to `/call` endpoints (updates *and* replicated queries) authenticated through such a chain are rejected during ingress validation with the new `RequestValidationError::UpdateCallNotPermittedByDelegation`; query and `read_state` requests remain permitted. * **Any other value** → because the field is a closed enum, an unsupported value fails to deserialize, so the request is **rejected when it is decoded** (before validation). There is no dedicated validator error for it. A restriction anywhere in the chain applies to the whole chain (like `targets`, restrictions only accumulate). The matching interface-spec change (dfinity/developer-docs#292) models the field as the union `Queries | All | Unrestricted`, which this enum mirrors exactly. ## Why this can't be bypassed, and why it's backward compatible * The field is part of the delegation's representation-independent hash, i.e. covered by the delegation signature. A dapp holding the session key **cannot strip the field** — doing so changes the hash and invalidates the signature. * **Old replicas fail closed**: they parse the delegation without the unknown field (serde ignores unknown map keys), recompute the hash without it, and signature verification fails. A restricted delegation is unusable on un-upgraded replicas rather than silently unrestricted. Rollout: replicas first, issuers (Internet Identity) after. * Delegations without the field are completely unaffected, so existing agents, dapps, and issuers need no changes. Agents only need to round-trip the new field once they want to carry restricted delegations; an agent that drops it fails closed. * The enforcement point is `HttpRequestVerifier<SignedIngressContent>` — the same verifier used by the ingress manager for block validation, so a malicious boundary node or replica cannot smuggle a restricted update call into a block. ## Rationale Enables issuers like Internet Identity to sign **read-only delegations**: sessions that can read on the user's behalf (query calls) but cannot change state (update calls), enforced by the protocol regardless of the client's cooperation. II sets this field from a "Read-only mode" checkbox in the authorize flow (II-side changes prepared separately). Historical precedent: the interface spec already extended the delegation map with an optional restriction field once — the `senders` field (added Dec 2021, never implemented by the replica, removed in [interface-spec#246](dfinity/interface-spec#246)). This PR follows the same formal pattern (`verify_delegations` accumulation), but implementation-first. ## Changes * `ic-types`: typed `DelegationPermissions { Queries, All }` enum (serialized as `"queries"`/`"all"`); optional `Delegation.permissions` field included in the signed bytes (`hash_of_map` key `"permissions"`); chainable `with_targets`/`with_permissions` builder methods (replacing the former `new_with_targets`/`new_with_permissions` constructors so a delegation can carry both). * `ic-validator`: `DelegationRestrictions` (targets + queries-only) threaded through delegation-chain validation; update-call rejection via `RequestValidationError::UpdateCallNotPermittedByDelegation`. The `/call` path uses the shared `validate_request_content` (no special-casing) and checks the restriction afterwards. * `ic-validator-ingress-message`: the new error variant mirrored. * `ic-validator-http-request-test-utils`: `delegate_to_with_permissions` chain-builder support. * Tests: signed-bytes golden test for the new field; CBOR encoding + round-trip tests; a decode-rejection test (`delegation_permissions_rejects_unsupported_value`) covering unsupported values including case/whitespace variants; validator-level e2e tests (update-rejected / query-and-read_state-permitted / `"all"`-permitted / restriction-mid-chain / combined targets+permissions); and a system test (`requests_with_delegation_permissions` in `rs/tests/crypto/ingress_verification_test.rs`, tagged `long_test`) exercising the full HTTP path against a real replica. ## Caveats / follow-ups * Calls to query methods submitted via the `/call` path (replicated queries) are rejected for `"queries"` delegations by design — the `"all"` vocabulary makes this explicit. * Interface-spec change in dfinity/developer-docs#292; II-side issuance and agent (`@icp-sdk/core`) round-tripping of the field prepared/pending. `cargo test -p ic-types -p ic-validator -p ic-validator-ingress-message` passes and clippy is clean; downstream consumers (`ic-http-endpoints-public`, `ic-ingress-manager`, `ic-canister-client`, `ic-state-machine-tests`) and both affected system-test binaries compile. https://claude.ai/code/session_01BQNgPJKgxWohrJ6owwJBzz --------- Co-authored-by: Claude <noreply@anthropic.com>
| <!-- Needs human verification: assign the final version number and replace "unreleased" with the release date when the permissions feature ships --> | ||
| ### 0.63.0 (unreleased) {$0_63_0} |
There was a problem hiding this comment.
The date should be updated and the PR merged when the feature is rolled out to all subnets (tentatively on Monday, July 6).
Summary
Extends the IC interface spec with the optional
permissionsfield of request delegations, as drafted in the replica implementation in dfinity/ic#10449.https-interface.md(Authentication): documents the newpermissionsfield of the delegation map."queries"restricts the delegation to query calls andread_staterequests; requests to/callendpoints are not accepted if any delegation in the chain carries this value, and a later delegation cannot lift the restriction."all"is the same as omitting the field. Any other value makes the delegation invalid for requests of any kind (fail-closed). Also addspermissionsto the string-typed field examples in the representation-independent hashing section, since the field is covered by the delegation signature.abstract-behavior.md(formal model): extendsSignedDelegationwithpermissions : Text | Unrestricted.verify_delegationsnow requires every delegation'spermissionsfield to hold a supported value, andverify_envelopefails for update calls (content of typeRequest) when any delegation in the chain is restricted to"queries". Becauseverify_envelopecan distinguish update calls from read-only requests by the type of the enclosed content, no changes to the nine call sites ofverify_envelopewere needed.changelog.md: adds a 0.63.0 entry visibly marked "unreleased"; an HTML comment instructs to assign the final version number and release date when the feature ships.Structural decisions
validate_delegationsinrs/validator/src/ingress_validation.rsof the draft implementation, including its test for a restriction sitting in the middle of a chain./callendpoints rather than "update calls" alone, so replicated queries (query methods submitted as update calls) are explicitly covered, matching the implementation which rejects at ingress validation.npm run buildpasses (209 pages).https://claude.ai/code/session_01WBqBka57Q7xYi4btZYfPqT