diff --git a/security-case-types/oidc-browser-relying-party/decision/build_intake.py b/security-case-types/oidc-browser-relying-party/decision/build_intake.py index baaf45e..e6c5254 100644 --- a/security-case-types/oidc-browser-relying-party/decision/build_intake.py +++ b/security-case-types/oidc-browser-relying-party/decision/build_intake.py @@ -13,7 +13,9 @@ from pathlib import Path HERE = Path(__file__).resolve().parent HUB_DECISION = "e418263e-a71f-4f01-a970-c87c9e187c09" -MEMO_ID = "nk-wp-0048-t05-oidc-browser-relying-party" +# r2 supersedes the first memo (delivered before ops-mason's review). A new id is +# needed: the dispatcher refuses new content under a delivered idempotency key. +MEMO_ID = "nk-wp-0048-t05-oidc-browser-relying-party-r2" ITEM = f"{MEMO_ID}-packet" packet = (HERE / "packet.md").read_text() @@ -45,7 +47,8 @@ envelope = { "NK-WP-0048-T05. Acknowledging admits the shape of browser OIDC clients for internal " "services, so that each later client is a light SecurityCase: accepted, materialized by " "ops-mason, followed through by ops-warden. It registers nothing and accepts no case; the " - "Forgejo case keeps its own acceptance. One attended operator session remains per case, " + "Forgejo case keeps its own acceptance. Supersedes memo " + "nk-wp-0048-t05-oidc-browser-relying-party; answer this one. One attended operator session remains per case, " "because KeyCape's live client registry is a Kubernetes Secret." ), "binding": { diff --git a/security-case-types/oidc-browser-relying-party/decision/intake.json b/security-case-types/oidc-browser-relying-party/decision/intake.json index bdc1f20..ade1951 100644 --- a/security-case-types/oidc-browser-relying-party/decision/intake.json +++ b/security-case-types/oidc-browser-relying-party/decision/intake.json @@ -1,7 +1,7 @@ { "kind": "informed-decision.memo-intake", "version": 1, - "idempotency_key": "state-hub.e418263e-a71f-4f01-a970-c87c9e187c09.nk-wp-0048-t05-oidc-browser-relying-party", + "idempotency_key": "state-hub.e418263e-a71f-4f01-a970-c87c9e187c09.nk-wp-0048-t05-oidc-browser-relying-party-r2", "memo_class": "decision-record", "review_group": "net-kingdom-admins", "drafted_by": "net-kingdom NK-WP-0048 (claude-code)", @@ -20,12 +20,12 @@ } }, "memo": { - "id": "nk-wp-0048-t05-oidc-browser-relying-party", + "id": "nk-wp-0048-t05-oidc-browser-relying-party-r2", "version": 1, "question": "Acknowledge oidc-browser-relying-party 1.0 as the reusable SecurityCaseType for internal web services that sign humans in through NetKingdom?", "requested_act": "approve", "binding_level": "organizational", - "brief": "NK-WP-0048-T05. Acknowledging admits the shape of browser OIDC clients for internal services, so that each later client is a light SecurityCase: accepted, materialized by ops-mason, followed through by ops-warden. It registers nothing and accepts no case; the Forgejo case keeps its own acceptance. One attended operator session remains per case, because KeyCape's live client registry is a Kubernetes Secret.", + "brief": "NK-WP-0048-T05. Acknowledging admits the shape of browser OIDC clients for internal services, so that each later client is a light SecurityCase: accepted, materialized by ops-mason, followed through by ops-warden. It registers nothing and accepts no case; the Forgejo case keeps its own acceptance. Supersedes memo nk-wp-0048-t05-oidc-browser-relying-party; answer this one. One attended operator session remains per case, because KeyCape's live client registry is a Kubernetes Secret.", "binding": { "justification": null, "principal": { @@ -47,15 +47,15 @@ "step_kind": "approve", "packet": [ { - "item_id": "nk-wp-0048-t05-oidc-browser-relying-party-packet", - "label": "nk-wp-0048-t05-oidc-browser-relying-party-packet", - "hash": "sha256:f3cbcfd4280fcd3716dfcb88b6b7a1c877ebf915f8e349dfc175e62e206ab52c" + "item_id": "nk-wp-0048-t05-oidc-browser-relying-party-r2-packet", + "label": "nk-wp-0048-t05-oidc-browser-relying-party-r2-packet", + "hash": "sha256:af6435e2009655c0a2ff2a1d3ae8777254b37621bf56c8930bf2a936653a5a8e" } ], "highlights": [ { - "id": "nk-wp-0048-t05-oidc-browser-relying-party-h1", - "item_id": "nk-wp-0048-t05-oidc-browser-relying-party-packet", + "id": "nk-wp-0048-t05-oidc-browser-relying-party-r2-h1", + "item_id": "nk-wp-0048-t05-oidc-browser-relying-party-r2-packet", "severity": "critical", "required_ack": true, "locator": { @@ -66,8 +66,8 @@ "note": "Acknowledging changes nothing live and accepts no case. It decides the shape every future browser client case will follow." }, { - "id": "nk-wp-0048-t05-oidc-browser-relying-party-h2", - "item_id": "nk-wp-0048-t05-oidc-browser-relying-party-packet", + "id": "nk-wp-0048-t05-oidc-browser-relying-party-r2-h2", + "item_id": "nk-wp-0048-t05-oidc-browser-relying-party-r2-packet", "severity": "warning", "required_ack": true, "locator": { @@ -85,9 +85,9 @@ }, "packet": [ { - "item_id": "nk-wp-0048-t05-oidc-browser-relying-party-packet", + "item_id": "nk-wp-0048-t05-oidc-browser-relying-party-r2-packet", "media_type": "text/markdown", - "content": "# Acknowledge SecurityCaseType oidc-browser-relying-party 1.0\n\nWorkplan: NK-WP-0048 (net-kingdom), task NK-WP-0048-T05\nHub decision: e418263e-a71f-4f01-a970-c87c9e187c09\nCanon: canon/standards/security-case-types_v0.1.md (accepted 2026-10-08), section 5\nType: security-case-types/oidc-browser-relying-party/ (type.yaml, README.md, tests)\n\n## Question\n\nAcknowledge oidc-browser-relying-party 1.0 as the reusable SecurityCaseType for\ninternal web services that sign humans in through NetKingdom?\n\n## One act\n\nMove the type from `proposed` to `acknowledged`. From then on, a browser OIDC\nclient for an internal service is a SecurityCase of this type: its owner fills in\nthe parameters, the case is accepted (owner and founder, risk class medium),\nops-mason materializes it and ops-warden follows through. The type becomes\n`implemented` only after its first case (Forgejo) is live and verified.\n\n## What acknowledging does not do\n\n- It registers no client, creates no group, mints no secret and changes nothing\n live.\n- It does not accept any case. The Forgejo case needs its own acceptance; its\n parameters were accepted in KEY-WP-0038-T01 and carry over.\n- It does not adopt the existing hand-built clients. Adoption, when it happens,\n does not change their live registrations.\n\n## The shape being acknowledged\n\nParameters: client id, display name, exact https redirect URIs (no wildcards),\nscopes from a closed list, client type (confidential or public), MFA pin, the\nLLDAP groups that gate sign-in and their initial members, the custody reference\nfor a confidential client's secret, the consuming service and its owner.\nSecrets never appear in a case, only `bao:` custody references.\n\nSteps, in order, each on its owner's entry point:\n\n| Step | Owner | Runs | Confidential only |\n| --- | --- | --- | --- |\n| ensure-groups | net-kingdom (identity-provisioner) | ops-warden | no |\n| mint-secret | railiance-platform (credential broker) | ops-warden | yes |\n| deliver-secret | railiance-platform (delivery into the sso namespace) | owner | yes |\n| bind-env | net-kingdom (KeyCape Deployment) | attended operator | yes |\n| register-client | net-kingdom (keycape-client.py) | attended operator | no |\n| hand-over | the consuming service's owner | owner | no |\n\nops-mason builds, for confidential clients, the custody path and two narrow read\npolicies (KeyCape and the consuming service). For every case it adds a catalog\nentry.\n\nVerification never reads a value. The authorization endpoint accepts each\nregistered redirect and refuses another; the groups exist; for confidential\nclients, a default identity is denied the custody path and the consumer's policy\nis allowed it. Rollback is the exact inverse, in reverse order.\n\n## Why this shape (alternatives rejected)\n\n- Per-client scripts (the status quo): too much manual work, one review surface\n per client.\n- Wildcard or prefix redirect URIs: the classic open-redirect weakness.\n- Automatic accounts for every NetKingdom user: sign-in is gated by explicit\n groups; the consumer decides account creation.\n- Secrets in case files for convenience: cases carry custody references only.\n\n## Attended steps\n\n`register-client` (and, for confidential clients, `bind-env`) need one attended\noperator session. KeyCape's live client registry is a Kubernetes Secret, and\nagents do not read or write Secrets. Removing this needs a KeyCape change\n(clients from a non-secret registry, secrets referenced from custody). That\nwould be a new version of this type, with its own decision.\n\n## Known limits of 1.0\n\nNo plain-http or loopback redirects (openbao-admin, the coulomb.social dev\nURIs), no resource audience, tenant binding or custom scopes\n(informed-decision-approver), no service clients. Each of these is a later\nversion or a separate type. Of the existing hand-built browser clients,\nuser-engine portal and vergabe fit 1.0 exactly.\n\n## Evidence\n\n- Validator and template: tools/security-case-types/, 32 tests\n- This type: 11 tests, including the Forgejo case checked field by field against\n the accepted request, and a regression showing that the vergabe case reproduces\n its hand-built registration exactly\n- Entry points: identity-provisioner groups ensure/revoke (39 tests),\n keycape-client.py (15 tests; all 71 KeyCape helper tests pass)\n- Declaration net-kingdom.keycape-client-registration passes the Playbook\n Capability Contract validator\n\n## Reviews before this decision\n\nops-mason (MASON-WP-0010-T01) and ops-warden (WARDEN-WP-0044-T01) review their\nsections of the type. Answering before their reviews are in is possible, but\nthen the type's acknowledgment covers sections its executors have not yet\nconfirmed.\n" + "content": "# Acknowledge SecurityCaseType oidc-browser-relying-party 1.0\n\nWorkplan: NK-WP-0048 (net-kingdom), task NK-WP-0048-T05\nHub decision: e418263e-a71f-4f01-a970-c87c9e187c09\nCanon: canon/standards/security-case-types_v0.1.md (accepted 2026-10-08), section 5\nType: security-case-types/oidc-browser-relying-party/ (type.yaml, README.md, tests)\nMemo: nk-wp-0048-t05-oidc-browser-relying-party-r2. It supersedes memo\nnk-wp-0048-t05-oidc-browser-relying-party, delivered earlier on 2026-10-08,\nwhich described the type before ops-mason's review. Please answer this one;\nboth serve the same hub decision, and the first answer resolves it.\n\n## Question\n\nAcknowledge oidc-browser-relying-party 1.0 as the reusable SecurityCaseType for\ninternal web services that sign humans in through NetKingdom?\n\n## One act\n\nMove the type from `proposed` to `acknowledged`. From then on, a browser OIDC\nclient for an internal service is a SecurityCase of this type: its owner fills in\nthe parameters, the case is accepted (owner and founder, risk class medium),\nops-mason materializes it and ops-warden follows through. The type becomes\n`implemented` only after its first case (Forgejo) is live and verified.\n\n## What acknowledging does not do\n\n- It registers no client, creates no group, mints no secret and changes nothing\n live.\n- It does not accept any case. The Forgejo case needs its own acceptance; its\n parameters were accepted in KEY-WP-0038-T01 and carry over, except custody:\n the type puts Forgejo's secret at `platform/workloads/keycape/forgejo`, not\n the request's proposed `platform/workloads/forgejo/netkingdom-oidc-client`.\n- It does not adopt the existing hand-built clients. Adoption, when it happens,\n does not change their live registrations.\n\n## The shape being acknowledged\n\nParameters: client id, display name, exact https redirect URIs (no wildcards),\nscopes from a closed list, client type (confidential or public), MFA pin, the\nLLDAP groups that gate sign-in and their initial members, the consuming service\nand its owner. A case cannot choose where a confidential client's secret lives:\nthe type derives `platform/workloads/keycape/`, as the live\nsecrets-engine-openbao client already uses. Secrets never appear in a case.\n\nSteps, in order, each on its owner's entry point:\n\n| Step | Owner | Runs | Confidential only |\n| --- | --- | --- | --- |\n| ensure-groups | net-kingdom (identity-provisioner) | ops-warden | no |\n| mint-secret | railiance-platform (credential broker) | ops-warden | yes |\n| deliver-secret | railiance-platform (delivery into the sso namespace) | owner | yes |\n| bind-env | net-kingdom (KeyCape Deployment) | attended operator | yes |\n| register-client | net-kingdom (keycape-client.py) | attended operator | no |\n| hand-over | the consuming service's owner | owner | no |\n\nops-mason builds, for confidential clients, the custody path; two read\npolicies in the existing family, `workload-kv-read-keycape--eso` and\n`workload-kv-read-keycape--`, each granting `read`\non that one data path and nothing else; and the Kubernetes-auth role\n`external-secrets-keycape-` that ESO uses to deliver the secret (its\nservice account only, 15m, no default policy). For every case it adds the\ncatalog entry `-keycape-client`. A case adds no new path\nlayout or policy family.\n\nVerification never reads a value. The authorization endpoint accepts each\nregistered redirect and refuses another; the groups exist; for confidential\nclients, a default identity is denied the custody path and the consumer's policy\nis allowed it. Rollback is the exact inverse, in reverse order.\n\n## Why this shape (alternatives rejected)\n\n- Per-client scripts (the status quo): too much manual work, one review surface\n per client.\n- Wildcard or prefix redirect URIs: the classic open-redirect weakness.\n- Automatic accounts for every NetKingdom user: sign-in is gated by explicit\n groups; the consumer decides account creation.\n- Secrets in case files for convenience: cases carry no secrets.\n- A custody path chosen per case: every case would add its own layout and\n policy family to review (rejected in ops-mason's review).\n\n## Attended steps\n\n`register-client` (and, for confidential clients, `bind-env`) need one attended\noperator session. KeyCape's live client registry is a Kubernetes Secret, and\nagents do not read or write Secrets. Removing this needs a KeyCape change\n(clients from a non-secret registry, secrets referenced from custody). That\nwould be a new version of this type, with its own decision.\n\n## Known limits of 1.0\n\nNo plain-http or loopback redirects (openbao-admin, the coulomb.social dev\nURIs), no resource audience, tenant binding or custom scopes\n(informed-decision-approver), no service clients. Each of these is a later\nversion or a separate type. Of the existing hand-built browser clients,\nuser-engine portal and vergabe fit 1.0 exactly.\n\n## Evidence\n\n- Validator: tools/security-case-types/, 19 tests (a policy or role must\n template its rules, not only its name)\n- This type: 12 tests, including the rendered Forgejo materialization (path,\n both policy bodies, ESO role, catalog entry), the Forgejo case checked field by field against\n the accepted request, and a regression showing that the vergabe case reproduces\n its hand-built registration exactly\n- Entry points: identity-provisioner groups ensure/revoke (39 tests),\n keycape-client.py (15 tests; it refuses a case-set custody path)\n- Declaration net-kingdom.keycape-client-registration passes the Playbook\n Capability Contract validator\n\n## Reviews before this decision\n\nops-mason reviewed the materialization section (MASON-WP-0010-T01) and asked\nfor a derived custody path, reuse of the existing policy family, templated\npolicy rules and the ESO role; all are in this version, and ops-mason finds the\nsection acceptable. ops-warden's review of the follow-through section\n(WARDEN-WP-0044-T01) is still in progress. Answering now acknowledges a\nfollow-through its executor has not yet confirmed.\n" } ] } diff --git a/security-case-types/oidc-browser-relying-party/decision/packet.md b/security-case-types/oidc-browser-relying-party/decision/packet.md index 5a83b45..d75c9d3 100644 --- a/security-case-types/oidc-browser-relying-party/decision/packet.md +++ b/security-case-types/oidc-browser-relying-party/decision/packet.md @@ -4,6 +4,10 @@ Workplan: NK-WP-0048 (net-kingdom), task NK-WP-0048-T05 Hub decision: e418263e-a71f-4f01-a970-c87c9e187c09 Canon: canon/standards/security-case-types_v0.1.md (accepted 2026-10-08), section 5 Type: security-case-types/oidc-browser-relying-party/ (type.yaml, README.md, tests) +Memo: nk-wp-0048-t05-oidc-browser-relying-party-r2. It supersedes memo +nk-wp-0048-t05-oidc-browser-relying-party, delivered earlier on 2026-10-08, +which described the type before ops-mason's review. Please answer this one; +both serve the same hub decision, and the first answer resolves it. ## Question @@ -23,7 +27,9 @@ ops-mason materializes it and ops-warden follows through. The type becomes - It registers no client, creates no group, mints no secret and changes nothing live. - It does not accept any case. The Forgejo case needs its own acceptance; its - parameters were accepted in KEY-WP-0038-T01 and carry over. + parameters were accepted in KEY-WP-0038-T01 and carry over, except custody: + the type puts Forgejo's secret at `platform/workloads/keycape/forgejo`, not + the request's proposed `platform/workloads/forgejo/netkingdom-oidc-client`. - It does not adopt the existing hand-built clients. Adoption, when it happens, does not change their live registrations. @@ -31,9 +37,10 @@ ops-mason materializes it and ops-warden follows through. The type becomes Parameters: client id, display name, exact https redirect URIs (no wildcards), scopes from a closed list, client type (confidential or public), MFA pin, the -LLDAP groups that gate sign-in and their initial members, the custody reference -for a confidential client's secret, the consuming service and its owner. -Secrets never appear in a case, only `bao:` custody references. +LLDAP groups that gate sign-in and their initial members, the consuming service +and its owner. A case cannot choose where a confidential client's secret lives: +the type derives `platform/workloads/keycape/`, as the live +secrets-engine-openbao client already uses. Secrets never appear in a case. Steps, in order, each on its owner's entry point: @@ -46,9 +53,14 @@ Steps, in order, each on its owner's entry point: | register-client | net-kingdom (keycape-client.py) | attended operator | no | | hand-over | the consuming service's owner | owner | no | -ops-mason builds, for confidential clients, the custody path and two narrow read -policies (KeyCape and the consuming service). For every case it adds a catalog -entry. +ops-mason builds, for confidential clients, the custody path; two read +policies in the existing family, `workload-kv-read-keycape--eso` and +`workload-kv-read-keycape--`, each granting `read` +on that one data path and nothing else; and the Kubernetes-auth role +`external-secrets-keycape-` that ESO uses to deliver the secret (its +service account only, 15m, no default policy). For every case it adds the +catalog entry `-keycape-client`. A case adds no new path +layout or policy family. Verification never reads a value. The authorization endpoint accepts each registered redirect and refuses another; the groups exist; for confidential @@ -62,7 +74,9 @@ is allowed it. Rollback is the exact inverse, in reverse order. - Wildcard or prefix redirect URIs: the classic open-redirect weakness. - Automatic accounts for every NetKingdom user: sign-in is gated by explicit groups; the consumer decides account creation. -- Secrets in case files for convenience: cases carry custody references only. +- Secrets in case files for convenience: cases carry no secrets. +- A custody path chosen per case: every case would add its own layout and + policy family to review (rejected in ops-mason's review). ## Attended steps @@ -82,18 +96,22 @@ user-engine portal and vergabe fit 1.0 exactly. ## Evidence -- Validator and template: tools/security-case-types/, 32 tests -- This type: 11 tests, including the Forgejo case checked field by field against +- Validator: tools/security-case-types/, 19 tests (a policy or role must + template its rules, not only its name) +- This type: 12 tests, including the rendered Forgejo materialization (path, + both policy bodies, ESO role, catalog entry), the Forgejo case checked field by field against the accepted request, and a regression showing that the vergabe case reproduces its hand-built registration exactly - Entry points: identity-provisioner groups ensure/revoke (39 tests), - keycape-client.py (15 tests; all 71 KeyCape helper tests pass) + keycape-client.py (15 tests; it refuses a case-set custody path) - Declaration net-kingdom.keycape-client-registration passes the Playbook Capability Contract validator ## Reviews before this decision -ops-mason (MASON-WP-0010-T01) and ops-warden (WARDEN-WP-0044-T01) review their -sections of the type. Answering before their reviews are in is possible, but -then the type's acknowledgment covers sections its executors have not yet -confirmed. +ops-mason reviewed the materialization section (MASON-WP-0010-T01) and asked +for a derived custody path, reuse of the existing policy family, templated +policy rules and the ESO role; all are in this version, and ops-mason finds the +section acceptable. ops-warden's review of the follow-through section +(WARDEN-WP-0044-T01) is still in progress. Answering now acknowledges a +follow-through its executor has not yet confirmed. diff --git a/workplans/NK-WP-0048-security-case-types.md b/workplans/NK-WP-0048-security-case-types.md index 7a0dbb9..89d4307 100644 --- a/workplans/NK-WP-0048-security-case-types.md +++ b/workplans/NK-WP-0048-security-case-types.md @@ -227,6 +227,16 @@ re-sent; and Forgejo's custody path moves from the accepted request's proposed railiance-apps (RAPPS-WP-0016-T02) need to adopt in T06. ops-warden's review (WARDEN-WP-0044-T01) is still in progress. +2026-10-08: revised memo `nk-wp-0048-t05-oidc-browser-relying-party-r2` built +(`packet.md`, `intake.json`, packet `sha256:af6435e2…`); it states that it +supersedes the first memo and passes informed-decision's and the dispatcher's +validation. It needs a new memo id because the dispatcher refuses new content +under a delivered idempotency key (`idempotency_conflict`). Queuing it is the +same attended step as above. Both memos serve hub decision `e418263e`; the +first answer resolves it, so the founder answers r2 and leaves the first memo +unanswered. key-cape and railiance-apps were told about Forgejo's new custody +path. + ```task id: NK-WP-0048-T06 status: wait