@pithy-sh/core 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/dist/audit/auditEvent.js +1 -1
- package/dist/audit/chain.d.ts +289 -0
- package/dist/audit/chain.d.ts.map +1 -0
- package/dist/audit/chain.js +400 -0
- package/dist/audit/citations.d.ts +52 -0
- package/dist/audit/citations.d.ts.map +1 -0
- package/dist/audit/citations.js +13 -0
- package/dist/capability/bindings.d.ts +22 -0
- package/dist/capability/bindings.d.ts.map +1 -1
- package/dist/capability/bindings.js +32 -2
- package/dist/capability/capability.d.ts +123 -16
- package/dist/capability/capability.d.ts.map +1 -1
- package/dist/capability/capability.js +17 -2
- package/dist/capability/erasure.d.ts +75 -0
- package/dist/capability/erasure.d.ts.map +1 -0
- package/dist/capability/erasure.js +2 -0
- package/dist/capability/manifest.d.ts +5 -0
- package/dist/capability/manifest.d.ts.map +1 -1
- package/dist/capability/personColumns.d.ts +28 -0
- package/dist/capability/personColumns.d.ts.map +1 -0
- package/dist/capability/personColumns.js +66 -0
- package/dist/capability/personRows.d.ts +62 -0
- package/dist/capability/personRows.d.ts.map +1 -0
- package/dist/capability/personRows.js +59 -0
- package/dist/capability/purge.d.ts +86 -19
- package/dist/capability/purge.d.ts.map +1 -1
- package/dist/capability/purge.js +83 -5
- package/dist/capability/settings.d.ts +10 -0
- package/dist/capability/settings.d.ts.map +1 -1
- package/dist/capability/vectorIndex.d.ts +36 -0
- package/dist/capability/vectorIndex.d.ts.map +1 -0
- package/dist/capability/vectorIndex.js +25 -0
- package/dist/cli/commandCatalog.generated.d.ts +433 -3
- package/dist/cli/commandCatalog.generated.d.ts.map +1 -1
- package/dist/cli/commandCatalog.generated.js +449 -2
- package/dist/cli/commandFacts.d.ts +8 -5
- package/dist/cli/commandFacts.d.ts.map +1 -1
- package/dist/cli/commandFacts.js +12 -3
- package/dist/consent/purpose.d.ts +70 -0
- package/dist/consent/purpose.d.ts.map +1 -0
- package/dist/consent/purpose.js +97 -0
- package/dist/consent/reader.d.ts +99 -0
- package/dist/consent/reader.d.ts.map +1 -0
- package/dist/consent/reader.js +33 -0
- package/dist/controlPlane/audit/actions.d.ts +15 -4
- package/dist/controlPlane/audit/actions.d.ts.map +1 -1
- package/dist/controlPlane/audit/actions.js +16 -5
- package/dist/controlPlane/capability.d.ts +10 -0
- package/dist/controlPlane/capability.d.ts.map +1 -1
- package/dist/controlPlane/capability.js +56 -8
- package/dist/controlPlane/context.d.ts +4 -0
- package/dist/controlPlane/context.d.ts.map +1 -1
- package/dist/controlPlane/context.js +3 -2
- package/dist/controlPlane/data/connection.d.ts +21 -2
- package/dist/controlPlane/data/connection.d.ts.map +1 -1
- package/dist/controlPlane/data/connection.js +13 -4
- package/dist/controlPlane/data/connections.d.ts +94 -0
- package/dist/controlPlane/data/connections.d.ts.map +1 -0
- package/dist/controlPlane/data/connections.js +151 -0
- package/dist/controlPlane/data/hold.d.ts +46 -0
- package/dist/controlPlane/data/hold.d.ts.map +1 -0
- package/dist/controlPlane/data/hold.js +36 -0
- package/dist/controlPlane/data/oidcTrust.d.ts +122 -0
- package/dist/controlPlane/data/oidcTrust.d.ts.map +1 -0
- package/dist/controlPlane/data/oidcTrust.js +173 -0
- package/dist/controlPlane/data/tables.d.ts +4 -0
- package/dist/controlPlane/data/tables.d.ts.map +1 -1
- package/dist/controlPlane/data/tables.js +9 -4
- package/dist/controlPlane/data/trustFirstCall.d.ts +16 -0
- package/dist/controlPlane/data/trustFirstCall.d.ts.map +1 -0
- package/dist/controlPlane/data/trustFirstCall.js +25 -0
- package/dist/controlPlane/discovery/adminRoute.d.ts +165 -2
- package/dist/controlPlane/discovery/adminRoute.d.ts.map +1 -1
- package/dist/controlPlane/discovery/adminRoute.js +32 -8
- package/dist/controlPlane/discovery/routeInput.d.ts +23 -0
- package/dist/controlPlane/discovery/routeInput.d.ts.map +1 -0
- package/dist/controlPlane/discovery/routeInput.js +80 -0
- package/dist/controlPlane/discovery/toolName.d.ts +21 -0
- package/dist/controlPlane/discovery/toolName.d.ts.map +1 -0
- package/dist/controlPlane/discovery/toolName.js +25 -0
- package/dist/controlPlane/erasure/attest.d.ts +78 -0
- package/dist/controlPlane/erasure/attest.d.ts.map +1 -0
- package/dist/controlPlane/erasure/attest.js +164 -0
- package/dist/controlPlane/erasure/read.d.ts +39 -0
- package/dist/controlPlane/erasure/read.d.ts.map +1 -0
- package/dist/controlPlane/erasure/read.js +140 -0
- package/dist/controlPlane/error/errors.d.ts +61 -2
- package/dist/controlPlane/error/errors.d.ts.map +1 -1
- package/dist/controlPlane/error/errors.js +13 -0
- package/dist/controlPlane/evidence/derive.d.ts +124 -0
- package/dist/controlPlane/evidence/derive.d.ts.map +1 -0
- package/dist/controlPlane/evidence/derive.js +343 -0
- package/dist/controlPlane/evidence/mapping.d.ts +85 -0
- package/dist/controlPlane/evidence/mapping.d.ts.map +1 -0
- package/dist/controlPlane/evidence/mapping.js +114 -0
- package/dist/controlPlane/evidence/pack.d.ts +46 -0
- package/dist/controlPlane/evidence/pack.d.ts.map +1 -0
- package/dist/controlPlane/evidence/pack.js +124 -0
- package/dist/controlPlane/http/connections.d.ts +96 -0
- package/dist/controlPlane/http/connections.d.ts.map +1 -0
- package/dist/controlPlane/http/connections.js +109 -0
- package/dist/controlPlane/http/guard.d.ts +23 -2
- package/dist/controlPlane/http/guard.d.ts.map +1 -1
- package/dist/controlPlane/http/guard.js +69 -12
- package/dist/controlPlane/http/handlers.d.ts +212 -10
- package/dist/controlPlane/http/handlers.d.ts.map +1 -1
- package/dist/controlPlane/http/handlers.js +117 -26
- package/dist/controlPlane/http/holds.d.ts +57 -0
- package/dist/controlPlane/http/holds.d.ts.map +1 -0
- package/dist/controlPlane/http/holds.js +98 -0
- package/dist/controlPlane/http/presence.d.ts +27 -0
- package/dist/controlPlane/http/presence.d.ts.map +1 -0
- package/dist/controlPlane/http/presence.js +67 -0
- package/dist/controlPlane/http/processing.d.ts +73 -0
- package/dist/controlPlane/http/processing.d.ts.map +1 -0
- package/dist/controlPlane/http/processing.js +17 -0
- package/dist/controlPlane/http/responses.d.ts +927 -3
- package/dist/controlPlane/http/responses.d.ts.map +1 -1
- package/dist/controlPlane/http/responses.js +283 -7
- package/dist/controlPlane/http/routes.d.ts +12 -2
- package/dist/controlPlane/http/routes.d.ts.map +1 -1
- package/dist/controlPlane/http/routes.js +182 -5
- package/dist/controlPlane/http/schemas.d.ts +63 -0
- package/dist/controlPlane/http/schemas.d.ts.map +1 -1
- package/dist/controlPlane/http/schemas.js +39 -1
- package/dist/controlPlane/http/verify.d.ts.map +1 -1
- package/dist/controlPlane/http/verify.js +13 -1
- package/dist/controlPlane/http/verifyTrust.d.ts +76 -0
- package/dist/controlPlane/http/verifyTrust.d.ts.map +1 -0
- package/dist/controlPlane/http/verifyTrust.js +75 -0
- package/dist/controlPlane/migrations/0004_connection_trusts.d.ts +15 -0
- package/dist/controlPlane/migrations/0004_connection_trusts.d.ts.map +1 -0
- package/dist/controlPlane/migrations/0004_connection_trusts.js +23 -0
- package/dist/controlPlane/migrations/0005_holds.d.ts +12 -0
- package/dist/controlPlane/migrations/0005_holds.d.ts.map +1 -0
- package/dist/controlPlane/migrations/0005_holds.js +26 -0
- package/dist/controlPlane/processing/derive.d.ts +77 -0
- package/dist/controlPlane/processing/derive.d.ts.map +1 -0
- package/dist/controlPlane/processing/derive.js +206 -0
- package/dist/controlPlane/processing/read.d.ts +10 -0
- package/dist/controlPlane/processing/read.d.ts.map +1 -0
- package/dist/controlPlane/processing/read.js +89 -0
- package/dist/controlPlane/scope/kind.d.ts +46 -0
- package/dist/controlPlane/scope/kind.d.ts.map +1 -0
- package/dist/controlPlane/scope/kind.js +52 -0
- package/dist/controlPlane/scope/scope.d.ts +58 -3
- package/dist/controlPlane/scope/scope.d.ts.map +1 -1
- package/dist/controlPlane/scope/scope.js +59 -4
- package/dist/controlPlane/token/jws.js +1 -1
- package/dist/controlPlane/token/mint.js +1 -1
- package/dist/controlPlane/wire.d.ts +23 -0
- package/dist/controlPlane/wire.d.ts.map +1 -1
- package/dist/controlPlane/wire.js +24 -1
- package/dist/createBackend.d.ts +13 -0
- package/dist/createBackend.d.ts.map +1 -1
- package/dist/createBackend.js +11 -3
- package/dist/createEntrypoint.d.ts +7 -0
- package/dist/createEntrypoint.d.ts.map +1 -1
- package/dist/createEntrypoint.js +4 -0
- package/dist/error/http.d.ts +168 -0
- package/dist/error/http.d.ts.map +1 -1
- package/dist/error/payload.d.ts +336 -0
- package/dist/error/payload.d.ts.map +1 -1
- package/dist/error/payload.js +112 -0
- package/dist/error/pithyError.d.ts +22 -0
- package/dist/error/pithyError.d.ts.map +1 -1
- package/dist/error/pithyError.js +31 -1
- package/dist/http/routeSpec.d.ts +46 -1
- package/dist/http/routeSpec.d.ts.map +1 -1
- package/dist/http/routeSpec.js +39 -5
- package/dist/http/verification.d.ts.map +1 -1
- package/dist/http/verification.js +1 -1
- package/dist/i18n/translator.d.ts +2 -1
- package/dist/i18n/translator.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/logger/record.d.ts +98 -0
- package/dist/logger/record.d.ts.map +1 -1
- package/dist/mcp/annotations.d.ts +20 -0
- package/dist/mcp/annotations.d.ts.map +1 -0
- package/dist/mcp/annotations.js +23 -0
- package/dist/mcp/tools.d.ts +75 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +118 -0
- package/dist/openapi/document.d.ts.map +1 -1
- package/dist/openapi/document.js +8 -26
- package/dist/openapi/operations.d.ts +35 -0
- package/dist/openapi/operations.d.ts.map +1 -0
- package/dist/openapi/operations.js +37 -0
- package/dist/presence/channel.d.ts +11 -0
- package/dist/presence/channel.d.ts.map +1 -0
- package/dist/presence/channel.js +65 -0
- package/dist/presence/declaration.d.ts +154 -0
- package/dist/presence/declaration.d.ts.map +1 -0
- package/dist/presence/declaration.js +164 -0
- package/dist/presence/durableObject.d.ts +61 -0
- package/dist/presence/durableObject.d.ts.map +1 -0
- package/dist/presence/durableObject.js +257 -0
- package/dist/presence/frames.d.ts +166 -0
- package/dist/presence/frames.d.ts.map +1 -0
- package/dist/presence/frames.js +90 -0
- package/dist/presence/http/guard.d.ts +11 -0
- package/dist/presence/http/guard.d.ts.map +1 -0
- package/dist/presence/http/guard.js +20 -0
- package/dist/presence/http/responses.d.ts +31 -0
- package/dist/presence/http/responses.d.ts.map +1 -0
- package/dist/presence/http/responses.js +34 -0
- package/dist/presence/http/routes.d.ts +24 -0
- package/dist/presence/http/routes.d.ts.map +1 -0
- package/dist/presence/http/routes.js +127 -0
- package/dist/presence/http/schemas.d.ts +29 -0
- package/dist/presence/http/schemas.d.ts.map +1 -0
- package/dist/presence/http/schemas.js +28 -0
- package/dist/presence/names.d.ts +23 -0
- package/dist/presence/names.d.ts.map +1 -0
- package/dist/presence/names.js +22 -0
- package/dist/presence/room.d.ts +95 -0
- package/dist/presence/room.d.ts.map +1 -0
- package/dist/presence/room.js +105 -0
- package/dist/presence/ticket.d.ts +55 -0
- package/dist/presence/ticket.d.ts.map +1 -0
- package/dist/presence/ticket.js +109 -0
- package/dist/privacy/classify.js +1 -1
- package/dist/privacy/encrypted.js +1 -1
- package/dist/processing/activity.d.ts +134 -0
- package/dist/processing/activity.d.ts.map +1 -0
- package/dist/processing/activity.js +161 -0
- package/dist/processing/composed.d.ts +18 -0
- package/dist/processing/composed.d.ts.map +1 -0
- package/dist/processing/composed.js +28 -0
- package/dist/processing/vocabulary.d.ts +26 -0
- package/dist/processing/vocabulary.d.ts.map +1 -0
- package/dist/processing/vocabulary.js +35 -0
- package/dist/queue/consumer.d.ts +59 -0
- package/dist/queue/consumer.d.ts.map +1 -0
- package/dist/queue/consumer.js +46 -0
- package/dist/retention/composed.d.ts +37 -0
- package/dist/retention/composed.d.ts.map +1 -0
- package/dist/retention/composed.js +66 -0
- package/dist/retention/holds.d.ts +61 -0
- package/dist/retention/holds.d.ts.map +1 -0
- package/dist/retention/holds.js +107 -0
- package/dist/retention/holdsPurge.d.ts +22 -0
- package/dist/retention/holdsPurge.d.ts.map +1 -0
- package/dist/retention/holdsPurge.js +50 -0
- package/dist/retention/policy.d.ts +132 -0
- package/dist/retention/policy.d.ts.map +1 -0
- package/dist/retention/policy.js +197 -0
- package/dist/session/rotated.d.ts +34 -0
- package/dist/session/rotated.d.ts.map +1 -0
- package/dist/session/rotated.js +25 -0
- package/dist/test-utils/connectionFixtures.d.ts +13 -0
- package/dist/test-utils/connectionFixtures.d.ts.map +1 -0
- package/dist/test-utils/connectionFixtures.js +58 -0
- package/dist/test-utils/interleave.d.ts +12 -0
- package/dist/test-utils/interleave.d.ts.map +1 -0
- package/dist/test-utils/interleave.js +26 -0
- package/dist/testWorker.d.ts +8 -0
- package/dist/testWorker.d.ts.map +1 -0
- package/dist/testWorker.js +9 -0
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/dist/workflow/dispatchRoute.js +1 -1
- package/package.json +1 -1
- package/src/audit/auditEvent.ts +1 -1
- package/src/audit/chain.ts +551 -0
- package/src/audit/citations.ts +63 -0
- package/src/capability/bindings.ts +61 -0
- package/src/capability/capability.ts +156 -19
- package/src/capability/erasure.ts +82 -0
- package/src/capability/personColumns.ts +83 -0
- package/src/capability/personRows.ts +125 -0
- package/src/capability/purge.ts +158 -21
- package/src/capability/settings.ts +7 -0
- package/src/capability/vectorIndex.ts +60 -0
- package/src/cli/commandCatalog.generated.ts +482 -2
- package/src/cli/commandFacts.ts +12 -3
- package/src/cloudflare-test.d.ts +3 -1
- package/src/consent/purpose.ts +160 -0
- package/src/consent/reader.ts +134 -0
- package/src/controlPlane/audit/actions.ts +15 -4
- package/src/controlPlane/capability.ts +99 -20
- package/src/controlPlane/context.ts +10 -2
- package/src/controlPlane/data/connection.ts +16 -2
- package/src/controlPlane/data/connections.ts +231 -0
- package/src/controlPlane/data/hold.ts +52 -0
- package/src/controlPlane/data/oidcTrust.ts +249 -0
- package/src/controlPlane/data/tables.ts +7 -0
- package/src/controlPlane/data/trustFirstCall.ts +37 -0
- package/src/controlPlane/discovery/adminRoute.ts +71 -9
- package/src/controlPlane/discovery/routeInput.ts +86 -0
- package/src/controlPlane/discovery/toolName.ts +23 -0
- package/src/controlPlane/erasure/attest.ts +205 -0
- package/src/controlPlane/erasure/read.ts +169 -0
- package/src/controlPlane/error/errors.ts +69 -2
- package/src/controlPlane/evidence/derive.ts +426 -0
- package/src/controlPlane/evidence/mapping.ts +118 -0
- package/src/controlPlane/evidence/pack.ts +190 -0
- package/src/controlPlane/http/connections.ts +129 -0
- package/src/controlPlane/http/guard.ts +118 -9
- package/src/controlPlane/http/handlers.ts +172 -40
- package/src/controlPlane/http/holds.ts +108 -0
- package/src/controlPlane/http/presence.ts +71 -0
- package/src/controlPlane/http/processing.ts +19 -0
- package/src/controlPlane/http/responses.ts +590 -7
- package/src/controlPlane/http/routes.ts +302 -3
- package/src/controlPlane/http/schemas.ts +126 -0
- package/src/controlPlane/http/verify.ts +19 -2
- package/src/controlPlane/http/verifyTrust.ts +176 -0
- package/src/controlPlane/migrations/0004_connection_trusts.ts +30 -0
- package/src/controlPlane/migrations/0005_holds.ts +40 -0
- package/src/controlPlane/processing/derive.ts +359 -0
- package/src/controlPlane/processing/read.ts +102 -0
- package/src/controlPlane/scope/kind.ts +63 -0
- package/src/controlPlane/scope/scope.ts +65 -3
- package/src/controlPlane/wire.ts +26 -0
- package/src/createBackend.ts +31 -6
- package/src/createEntrypoint.ts +17 -0
- package/src/error/payload.ts +234 -0
- package/src/error/pithyError.ts +42 -0
- package/src/http/routeSpec.ts +75 -5
- package/src/http/verification.ts +3 -1
- package/src/i18n/translator.ts +2 -1
- package/src/index.ts +2 -0
- package/src/mcp/annotations.ts +38 -0
- package/src/mcp/tools.ts +220 -0
- package/src/openapi/document.ts +8 -28
- package/src/openapi/operations.ts +62 -0
- package/src/presence/channel.ts +46 -0
- package/src/presence/declaration.ts +283 -0
- package/src/presence/durableObject.ts +287 -0
- package/src/presence/frames.ts +153 -0
- package/src/presence/http/guard.ts +23 -0
- package/src/presence/http/responses.ts +63 -0
- package/src/presence/http/routes.ts +165 -0
- package/src/presence/http/schemas.ts +39 -0
- package/src/presence/names.ts +45 -0
- package/src/presence/room.ts +145 -0
- package/src/presence/ticket.ts +145 -0
- package/src/processing/activity.ts +289 -0
- package/src/processing/composed.ts +33 -0
- package/src/processing/vocabulary.ts +43 -0
- package/src/queue/consumer.ts +104 -0
- package/src/retention/composed.ts +75 -0
- package/src/retention/holds.ts +175 -0
- package/src/retention/holdsPurge.ts +50 -0
- package/src/retention/policy.ts +305 -0
- package/src/session/rotated.ts +53 -0
- package/src/test-utils/connectionFixtures.ts +58 -0
- package/src/test-utils/interleave.ts +31 -0
- package/src/testWorker.ts +13 -0
- package/src/version.generated.ts +1 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { ControlPlaneKeyConflictError } from "../error/errors.js";
|
|
4
|
+
import { JsonDate } from "../../data/codecs.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
//#region src/controlPlane/data/oidcTrust.ts
|
|
7
|
+
/**
|
|
8
|
+
* An OIDC trust: the second credential kind a connection may hold, beside a registered key
|
|
9
|
+
* (`docs/AGENT-ACCESS.md` §6.2, #722).
|
|
10
|
+
*
|
|
11
|
+
* A key is a bearer: one client holds the private half. A trust is a **policy**: a trusted issuer, the
|
|
12
|
+
* audience its tokens must name, and a matcher over the claims that say which workload a token was minted
|
|
13
|
+
* for. Nobody holds a secret. The workload asks its own identity provider for a short-lived token, and the
|
|
14
|
+
* Worker checks it against the issuer's published keys with the verifier the webhooks already use,
|
|
15
|
+
* `core/src/http/oidcWebhook.ts`.
|
|
16
|
+
*
|
|
17
|
+
* ## The claims become data here, and that is a deliberate departure
|
|
18
|
+
*
|
|
19
|
+
* In a webhook the claims check is a predicate the **route** holds, because whoever wrote the route knows
|
|
20
|
+
* the legitimate sender. A connection's trust is per adopter, decided at registration by the same person who
|
|
21
|
+
* grants the scopes, so the matcher is stored with the trust and validated on the way in. That is the whole
|
|
22
|
+
* reason {@link OidcClaimMatch} exists rather than a function.
|
|
23
|
+
*
|
|
24
|
+
* ## The claims check is the boundary, not the signature
|
|
25
|
+
*
|
|
26
|
+
* An identity provider signs for everybody with the same keys. GitHub signs a token for every repository on
|
|
27
|
+
* the planet. A trust whose matcher constrains nothing accepts all of them, so {@link OidcClaimMatch} refuses
|
|
28
|
+
* one at registration — structurally, not as a warning — and refuses the claims that name the token rather
|
|
29
|
+
* than the workload ({@link RESERVED_OIDC_CLAIMS}), since a matcher made of those constrains nothing either.
|
|
30
|
+
*
|
|
31
|
+
* ## Named claims, not the `sub` string
|
|
32
|
+
*
|
|
33
|
+
* A `sub` is a concatenation whose composition belongs to the provider and to the adopter's own template.
|
|
34
|
+
* The claims beneath it — GitHub's `repository_id`, `environment`, `ref`; GitLab's `project_id` — are
|
|
35
|
+
* published individually and are stable. So the matcher is a map of claim names to values, and `sub` is one
|
|
36
|
+
* claim among the others rather than a special case.
|
|
37
|
+
*
|
|
38
|
+
* ## No rotation, and revocation changes shape
|
|
39
|
+
*
|
|
40
|
+
* There is no key to append, prove and expire. A token cannot be revoked; the trust is, and every token
|
|
41
|
+
* already minted stays live until it expires — {@link TRUST_REVOCATION_LATENCY} is the sentence that says
|
|
42
|
+
* so at the moment it matters.
|
|
43
|
+
*
|
|
44
|
+
* Nothing in this module reads a clock, a binding or D1. `now` is injected wherever time matters.
|
|
45
|
+
*/
|
|
46
|
+
/**
|
|
47
|
+
* Claims that name the token rather than the workload: who minted it, for whom, and when. A matcher built
|
|
48
|
+
* from these alone would accept every workload the issuer serves, so none of them may be registered.
|
|
49
|
+
* `iss` and `aud` are checked anyway, by the trust's own issuer and audience.
|
|
50
|
+
*/
|
|
51
|
+
const RESERVED_OIDC_CLAIMS = [
|
|
52
|
+
"iss",
|
|
53
|
+
"aud",
|
|
54
|
+
"exp",
|
|
55
|
+
"nbf",
|
|
56
|
+
"iat",
|
|
57
|
+
"jti"
|
|
58
|
+
];
|
|
59
|
+
/** The most claims one trust may match on. Far above any real constraint; a bound on a stored row. */
|
|
60
|
+
const MAX_TRUST_CLAIMS = 16;
|
|
61
|
+
/** The most claims a refusal records, so a token carrying a hundred cannot fill the audit trail. */
|
|
62
|
+
const MAX_OBSERVED_CLAIMS = 32;
|
|
63
|
+
/** The longest claim value a matcher stores or a refusal records. */
|
|
64
|
+
const MAX_CLAIM_VALUE = 256;
|
|
65
|
+
/** A claim name: the plain alphabet providers use, plus `.`, `:` and `-` for a customized template. */
|
|
66
|
+
const CLAIM_NAME = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
67
|
+
/** Every C0 and C1 control, the line separators, and the bidi controls: a value is one line of plain text. */
|
|
68
|
+
const UNRENDERABLE = /[\p{Cc}\p{Bidi_Control}\u2028\u2029]/u;
|
|
69
|
+
const reserved = new Set(RESERVED_OIDC_CLAIMS);
|
|
70
|
+
/**
|
|
71
|
+
* One claim name in a matcher. A name, not a path: nested claims are not matched, because a matcher that
|
|
72
|
+
* walks a structure is a matcher with more ways to be wrong on the check the whole trust rests on.
|
|
73
|
+
*/
|
|
74
|
+
const OidcClaimName = z.string().regex(CLAIM_NAME, "A claim name is letters, digits, `_`, `.`, `:` or `-`, at most 64.").refine((name) => !reserved.has(name), { message: `${RESERVED_OIDC_CLAIMS.join(", ")} name the token, not the workload, so they cannot constrain a trust.` }).describe("A claim the token must carry, by name — `repository_id`, `environment`, `project_id`, `sub`. Never one of the claims that name the token itself.");
|
|
75
|
+
/** One claim value in a matcher: compared as a string against the token's claim of the same name. */
|
|
76
|
+
const OidcClaimValue = z.string().min(1, "A claim value cannot be empty.").max(256, `A claim value is 256 characters at most.`).refine((value) => !UNRENDERABLE.test(value), "A claim value is one line of plain text.").describe("The exact value that claim must have. A numeric or boolean claim is compared by its string form, so `1262274726` matches a `repository_id` whichever way the provider typed it.");
|
|
77
|
+
/**
|
|
78
|
+
* Which workloads a trust accepts: every named claim must equal its value.
|
|
79
|
+
*
|
|
80
|
+
* **At least one, and that is the refusal the whole credential kind rests on.** An issuer with no claim
|
|
81
|
+
* constraint accepts every workload it serves.
|
|
82
|
+
*/
|
|
83
|
+
const OidcClaimMatch = z.record(OidcClaimName, OidcClaimValue).refine((claims) => Object.keys(claims).length > 0, { message: "A trust needs at least one claim constraint. An issuer alone accepts every workload it serves." }).refine((claims) => Object.keys(claims).length <= 16, { message: `A trust matches on 16 claims at most.` }).describe("The subject constraint: every named claim must equal its value. At least one, because an issuer alone accepts every workload it serves.");
|
|
84
|
+
/** An https URL. A trust's issuer and key endpoint are fetched from, so plain http is refused. */
|
|
85
|
+
const HttpsUrl = z.url({ protocol: /^https$/ });
|
|
86
|
+
/**
|
|
87
|
+
* One registered OIDC trust, an element of the connection's `trusts` column.
|
|
88
|
+
*
|
|
89
|
+
* Stored beside `keys` rather than inside it: the two kinds share nothing but the connection they grant
|
|
90
|
+
* through, and a union array would make every key reader learn about trusts.
|
|
91
|
+
*/
|
|
92
|
+
const OidcTrust = z.object({
|
|
93
|
+
trustId: z.string().min(1).max(64).describe("The trust's id, named by a call on the `pithy-control-plane-trust` header the way a token's `kid` names a key. Not a secret."),
|
|
94
|
+
issuer: HttpsUrl.describe("The exact `iss` this trust accepts — `https://token.actions.githubusercontent.com`, `https://gitlab.com`. A token from any other issuer is refused before its claims are read."),
|
|
95
|
+
jwksUrl: HttpsUrl.describe("Where the issuer publishes the keys that sign its tokens, read from its discovery document at registration. Public, so configuration and not a secret."),
|
|
96
|
+
audience: z.string().min(1).max(512).describe("The `aud` a token must name. Defaults to this environment's own Worker origin, which is what stops a staging token being replayed against production."),
|
|
97
|
+
claims: OidcClaimMatch,
|
|
98
|
+
authorizedBy: z.email().max(254).describe("Who sanctioned this trust: the signed-in session's email at registration. Never who may use it — a trust's users are whoever its claims admit."),
|
|
99
|
+
validFrom: JsonDate.describe("When the trust was registered and began to be honored."),
|
|
100
|
+
revokedAt: JsonDate.nullable().describe("When the trust was revoked, or null. Takes effect on the next call; a token already minted is refused from then on, but stays in its holder's hands until it expires."),
|
|
101
|
+
firstCallAt: JsonDate.nullable().describe("When a call first verified under this trust, or null while it is awaiting a first call. Nothing can prove a trust at registration — there is no private key here and no workload present — so this is the only proof there is.")
|
|
102
|
+
}).describe("One OIDC trust: a trusted issuer, the audience its tokens must name, and the claims that say which workloads it admits.");
|
|
103
|
+
/**
|
|
104
|
+
* What a caller should expect after a trust is revoked, said at the moment it is revoked rather than
|
|
105
|
+
* discovered afterwards.
|
|
106
|
+
*/
|
|
107
|
+
const TRUST_REVOCATION_LATENCY = "A token cannot be revoked, only the trust. Every call naming this trust is refused from now on, but a token already minted stays in its holder's hands until it expires — the issuer's own lifetime, typically minutes.";
|
|
108
|
+
/** Every trust that may verify a call at `now`: not revoked, and started. */
|
|
109
|
+
function activeTrusts(trusts, now) {
|
|
110
|
+
return trusts.filter((trust) => trust.revokedAt === null && trust.validFrom.getTime() <= now.getTime());
|
|
111
|
+
}
|
|
112
|
+
/** The trust a call names, if it is live at `now`, or null. The revocation check lives here so no caller forgets it. */
|
|
113
|
+
function findVerifyingTrust(trusts, trustId, now) {
|
|
114
|
+
return activeTrusts(trusts, now).find((trust) => trust.trustId === trustId) ?? null;
|
|
115
|
+
}
|
|
116
|
+
/** Register a trust beside the others. A duplicate id is refused: the id is what a call addresses. */
|
|
117
|
+
function appendTrust(trusts, trust) {
|
|
118
|
+
if (trusts.some((existing) => existing.trustId === trust.trustId)) throw new ControlPlaneKeyConflictError({
|
|
119
|
+
message: "That trust id is already registered on this connection.",
|
|
120
|
+
action: "Register the trust under a new id.",
|
|
121
|
+
detail: `duplicate trustId ${trust.trustId}`
|
|
122
|
+
});
|
|
123
|
+
return [...trusts, trust];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Revoke one trust at `now`, or answer null when no trust answers to that id. A trust already revoked keeps
|
|
127
|
+
* the instant it was first revoked: the window a call was refused under must not move.
|
|
128
|
+
*/
|
|
129
|
+
function revokeTrust(trusts, trustId, now) {
|
|
130
|
+
if (!trusts.some((trust) => trust.trustId === trustId)) return null;
|
|
131
|
+
return trusts.map((trust) => trust.trustId === trustId && trust.revokedAt === null ? {
|
|
132
|
+
...trust,
|
|
133
|
+
revokedAt: now
|
|
134
|
+
} : trust);
|
|
135
|
+
}
|
|
136
|
+
/** A claim's string form, or undefined for a value that is not a string, number or boolean. */
|
|
137
|
+
function claimString(value) {
|
|
138
|
+
if (typeof value === "string") return value;
|
|
139
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
140
|
+
if (typeof value === "boolean") return String(value);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Whether a token's claims satisfy a trust's matcher: every registered claim present and equal.
|
|
144
|
+
*
|
|
145
|
+
* An empty matcher matches nothing. Registration refuses one, and this refuses it again rather than letting
|
|
146
|
+
* a hand-edited row vacuously admit every workload.
|
|
147
|
+
*/
|
|
148
|
+
function claimsMatch(match, claims) {
|
|
149
|
+
const entries = Object.entries(match);
|
|
150
|
+
if (entries.length === 0) return false;
|
|
151
|
+
return entries.every(([name, expected]) => claimString(claims[name]) === expected);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The claims a refused token carried, as a refusal records them: the workload's claims, as strings, capped.
|
|
155
|
+
*
|
|
156
|
+
* This is what makes a custom issuer workable and a profiled one self-correcting — register, let the
|
|
157
|
+
* workload run once, read what it actually sent. Only ever called once the signature, issuer and audience
|
|
158
|
+
* have verified, so the caller demonstrably holds a genuine token for this audience and naming its claims
|
|
159
|
+
* leaks nothing it does not already hold.
|
|
160
|
+
*/
|
|
161
|
+
function observedClaims(claims) {
|
|
162
|
+
const recorded = {};
|
|
163
|
+
for (const [name, value] of Object.entries(claims)) {
|
|
164
|
+
if (Object.keys(recorded).length >= 32) break;
|
|
165
|
+
if (reserved.has(name) || !CLAIM_NAME.test(name)) continue;
|
|
166
|
+
const text = claimString(value);
|
|
167
|
+
if (text === void 0) continue;
|
|
168
|
+
recorded[name] = text.slice(0, 256);
|
|
169
|
+
}
|
|
170
|
+
return recorded;
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
export { MAX_CLAIM_VALUE, MAX_OBSERVED_CLAIMS, MAX_TRUST_CLAIMS, OidcClaimMatch, OidcClaimName, OidcClaimValue, OidcTrust, RESERVED_OIDC_CLAIMS, TRUST_REVOCATION_LATENCY, activeTrusts, appendTrust, claimsMatch, findVerifyingTrust, observedClaims, revokeTrust };
|
|
@@ -5,6 +5,7 @@ import type { Kysely } from "kysely";
|
|
|
5
5
|
import type { z } from "zod";
|
|
6
6
|
import { type DatabaseSchema } from "../../data/db";
|
|
7
7
|
import { ControlPlaneConnection } from "./connection";
|
|
8
|
+
import { LegalHold } from "./hold";
|
|
8
9
|
import { ControlPlaneReplay } from "./replay";
|
|
9
10
|
/**
|
|
10
11
|
* The connections table. `CamelCasePlugin` snake-cases it to `pithy_controlplane_connections` — the
|
|
@@ -19,6 +20,8 @@ export declare const CONTROL_PLANE_CONNECTIONS_TABLE = "pithyControlplaneConnect
|
|
|
19
20
|
* makes the claim a primary-key insert and therefore strongly consistent. See `../replay/guard.ts`.
|
|
20
21
|
*/
|
|
21
22
|
export declare const CONTROL_PLANE_REPLAYS_TABLE = "pithyControlplaneReplays";
|
|
23
|
+
/** Legal holds, kept after release as the record that one stood. */
|
|
24
|
+
export declare const CONTROL_PLANE_HOLDS_TABLE = "pithyControlplaneHolds";
|
|
22
25
|
/**
|
|
23
26
|
* The control-plane tables map. Both are always present: a Worker that composes the seam and has never
|
|
24
27
|
* been connected still needs somewhere for the answer "nobody is connected" to come from, and the
|
|
@@ -29,6 +32,7 @@ export declare function controlPlaneTables(): Record<string, z.ZodObject>;
|
|
|
29
32
|
export type ControlPlaneTables = {
|
|
30
33
|
[CONTROL_PLANE_CONNECTIONS_TABLE]: typeof ControlPlaneConnection;
|
|
31
34
|
[CONTROL_PLANE_REPLAYS_TABLE]: typeof ControlPlaneReplay;
|
|
35
|
+
[CONTROL_PLANE_HOLDS_TABLE]: typeof LegalHold;
|
|
32
36
|
};
|
|
33
37
|
export type ControlPlaneDatabase = Kysely<DatabaseSchema<ControlPlaneTables>>;
|
|
34
38
|
/** Build the control-plane database from the `DB` binding (CamelCasePlugin installed). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/data/tables.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,iCAAiC,CAAC;AAE9E;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,6BAA6B,CAAC;AAEtE;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/data/tables.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,iCAAiC,CAAC;AAE9E;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,6BAA6B,CAAC;AAEtE,oEAAoE;AACpE,eAAO,MAAM,yBAAyB,2BAA2B,CAAC;AAElE;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAMhE;AAED,+DAA+D;AAC/D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,CAAC,+BAA+B,CAAC,EAAE,OAAO,sBAAsB,CAAC;IACjE,CAAC,2BAA2B,CAAC,EAAE,OAAO,kBAAkB,CAAC;IACzD,CAAC,yBAAyB,CAAC,EAAE,OAAO,SAAS,CAAC;CAC/C,CAAC;AACF,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAE9E,0FAA0F;AAC1F,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,UAAU,GAAG,oBAAoB,CAMzE"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
|
-
import {
|
|
3
|
+
import { LegalHold } from "./hold.js";
|
|
4
4
|
import { createDatabase } from "../../data/db.js";
|
|
5
|
+
import { ControlPlaneConnection } from "./connection.js";
|
|
5
6
|
import { ControlPlaneReplay } from "./replay.js";
|
|
6
7
|
//#region src/controlPlane/data/tables.ts
|
|
7
8
|
/**
|
|
@@ -17,6 +18,8 @@ const CONTROL_PLANE_CONNECTIONS_TABLE = "pithyControlplaneConnections";
|
|
|
17
18
|
* makes the claim a primary-key insert and therefore strongly consistent. See `../replay/guard.ts`.
|
|
18
19
|
*/
|
|
19
20
|
const CONTROL_PLANE_REPLAYS_TABLE = "pithyControlplaneReplays";
|
|
21
|
+
/** Legal holds, kept after release as the record that one stood. */
|
|
22
|
+
const CONTROL_PLANE_HOLDS_TABLE = "pithyControlplaneHolds";
|
|
20
23
|
/**
|
|
21
24
|
* The control-plane tables map. Both are always present: a Worker that composes the seam and has never
|
|
22
25
|
* been connected still needs somewhere for the answer "nobody is connected" to come from, and the
|
|
@@ -25,15 +28,17 @@ const CONTROL_PLANE_REPLAYS_TABLE = "pithyControlplaneReplays";
|
|
|
25
28
|
function controlPlaneTables() {
|
|
26
29
|
return {
|
|
27
30
|
[CONTROL_PLANE_CONNECTIONS_TABLE]: ControlPlaneConnection,
|
|
28
|
-
[CONTROL_PLANE_REPLAYS_TABLE]: ControlPlaneReplay
|
|
31
|
+
[CONTROL_PLANE_REPLAYS_TABLE]: ControlPlaneReplay,
|
|
32
|
+
[CONTROL_PLANE_HOLDS_TABLE]: LegalHold
|
|
29
33
|
};
|
|
30
34
|
}
|
|
31
35
|
/** Build the control-plane database from the `DB` binding (CamelCasePlugin installed). */
|
|
32
36
|
function controlPlaneDatabase(d1) {
|
|
33
37
|
return createDatabase(d1, {
|
|
34
38
|
[CONTROL_PLANE_CONNECTIONS_TABLE]: ControlPlaneConnection,
|
|
35
|
-
[CONTROL_PLANE_REPLAYS_TABLE]: ControlPlaneReplay
|
|
39
|
+
[CONTROL_PLANE_REPLAYS_TABLE]: ControlPlaneReplay,
|
|
40
|
+
[CONTROL_PLANE_HOLDS_TABLE]: LegalHold
|
|
36
41
|
});
|
|
37
42
|
}
|
|
38
43
|
//#endregion
|
|
39
|
-
export { CONTROL_PLANE_CONNECTIONS_TABLE, CONTROL_PLANE_REPLAYS_TABLE, controlPlaneDatabase, controlPlaneTables };
|
|
44
|
+
export { CONTROL_PLANE_CONNECTIONS_TABLE, CONTROL_PLANE_HOLDS_TABLE, CONTROL_PLANE_REPLAYS_TABLE, controlPlaneDatabase, controlPlaneTables };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { type ControlPlaneDatabase } from "./tables";
|
|
4
|
+
/**
|
|
5
|
+
* Record the first call a trust answered (#722), so a listing stops saying *awaiting a first call*.
|
|
6
|
+
*
|
|
7
|
+
* **One statement that touches one field, never a read-modify-write of the column.** The verifier read the row
|
|
8
|
+
* moments ago, and an operator may have revoked the trust since. Writing back the whole `trusts` array as the
|
|
9
|
+
* verifier saw it would resurrect that trust — a revocation undone by the very call it was meant to stop. So the
|
|
10
|
+
* write is `json_set` on the matching element's `firstCallAt` alone, and only while that element is still
|
|
11
|
+
* unrevoked and still uncalled: a revoked trust is left exactly as revoked, and a second first call changes nothing.
|
|
12
|
+
*
|
|
13
|
+
* The instant is written as ISO-8601, the shape `JsonDate` stores inside the column.
|
|
14
|
+
*/
|
|
15
|
+
export declare function recordTrustFirstCall(db: ControlPlaneDatabase, connectionId: string, trustId: string, at: Date): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=trustFirstCall.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trustFirstCall.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/data/trustFirstCall.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmC,KAAK,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAEtF;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,oBAAoB,EACxB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,IAAI,GACP,OAAO,CAAC,IAAI,CAAC,CAcf"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { CONTROL_PLANE_CONNECTIONS_TABLE } from "./tables.js";
|
|
4
|
+
import { sql } from "kysely";
|
|
5
|
+
//#region src/controlPlane/data/trustFirstCall.ts
|
|
6
|
+
/**
|
|
7
|
+
* Record the first call a trust answered (#722), so a listing stops saying *awaiting a first call*.
|
|
8
|
+
*
|
|
9
|
+
* **One statement that touches one field, never a read-modify-write of the column.** The verifier read the row
|
|
10
|
+
* moments ago, and an operator may have revoked the trust since. Writing back the whole `trusts` array as the
|
|
11
|
+
* verifier saw it would resurrect that trust — a revocation undone by the very call it was meant to stop. So the
|
|
12
|
+
* write is `json_set` on the matching element's `firstCallAt` alone, and only while that element is still
|
|
13
|
+
* unrevoked and still uncalled: a revoked trust is left exactly as revoked, and a second first call changes nothing.
|
|
14
|
+
*
|
|
15
|
+
* The instant is written as ISO-8601, the shape `JsonDate` stores inside the column.
|
|
16
|
+
*/
|
|
17
|
+
async function recordTrustFirstCall(db, connectionId, trustId, at) {
|
|
18
|
+
const trusts = sql.ref("trusts");
|
|
19
|
+
const uncalled = (alias) => sql`json_extract(${sql.ref(`${alias}.value`)}, '$.trustId') = ${trustId}
|
|
20
|
+
and json_extract(${sql.ref(`${alias}.value`)}, '$.revokedAt') is null
|
|
21
|
+
and json_extract(${sql.ref(`${alias}.value`)}, '$.firstCallAt') is null`;
|
|
22
|
+
await db.updateTable(CONTROL_PLANE_CONNECTIONS_TABLE).set({ trusts: sql`json_set(${trusts}, '$[' || (select ${sql.ref("t.key")} from json_each(${trusts}) as t where ${uncalled("t")} limit 1) || '].firstCallAt', ${at.toISOString()})` }).where("id", "=", connectionId).where(sql`exists (select 1 from json_each(${trusts}) as u where ${uncalled("u")})`).execute();
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { recordTrustFirstCall };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 Pithy
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { ScopeKind } from "../scope/kind";
|
|
5
|
+
import { ControlPlaneScope } from "../scope/scope";
|
|
4
6
|
/**
|
|
5
7
|
* What a capability tells a management client about its own admin surface.
|
|
6
8
|
*
|
|
@@ -28,6 +30,24 @@ export declare const AdminRouteMethod: z.ZodEnum<{
|
|
|
28
30
|
PUT: "PUT";
|
|
29
31
|
}>;
|
|
30
32
|
export type AdminRouteMethod = z.infer<typeof AdminRouteMethod>;
|
|
33
|
+
/**
|
|
34
|
+
* An admin route's operation id (`docs/AGENT-ACCESS.md` §5): lowercase words joined by `.`, `_` or `-`.
|
|
35
|
+
*
|
|
36
|
+
* **Permanent once published.** An agent names a tool in prompts, saved workflows and its own record of what
|
|
37
|
+
* worked, so a renamed operation breaks callers silently — the same class of rule as a role name. Unique within
|
|
38
|
+
* its capability; `packages/cli/src/ci/adminOperations.test.ts` holds every kit capability's to a table.
|
|
39
|
+
*/
|
|
40
|
+
export declare const AdminOperation: z.ZodString;
|
|
41
|
+
export type AdminOperation = z.infer<typeof AdminOperation>;
|
|
42
|
+
/**
|
|
43
|
+
* An admin route's input as JSON Schema: an object whose `path`, `query` and `body` properties are each
|
|
44
|
+
* the JSON Schema of the validator the route runs on that part of the request.
|
|
45
|
+
*
|
|
46
|
+
* A record rather than a modeled schema, because it is JSON Schema — a vocabulary this manifest relays, never
|
|
47
|
+
* one it defines — and an MCP client publishes it verbatim as a tool's `inputSchema`.
|
|
48
|
+
*/
|
|
49
|
+
export declare const AdminRouteInput: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
50
|
+
export type AdminRouteInput = z.infer<typeof AdminRouteInput>;
|
|
31
51
|
/** One admin route a capability contributes behind the `control-plane` strategy. */
|
|
32
52
|
export declare const AdminRoute: z.ZodObject<{
|
|
33
53
|
method: z.ZodEnum<{
|
|
@@ -40,8 +60,29 @@ export declare const AdminRoute: z.ZodObject<{
|
|
|
40
60
|
path: z.ZodString;
|
|
41
61
|
scope: z.ZodNullable<z.ZodString>;
|
|
42
62
|
summary: z.ZodString;
|
|
63
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
64
|
+
charges: "charges";
|
|
65
|
+
credentials: "credentials";
|
|
66
|
+
deletes: "deletes";
|
|
67
|
+
reads: "reads";
|
|
68
|
+
sends: "sends";
|
|
69
|
+
writes: "writes";
|
|
70
|
+
}>>;
|
|
71
|
+
operation: z.ZodOptional<z.ZodString>;
|
|
72
|
+
input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
43
73
|
}, z.core.$strip>;
|
|
44
74
|
export type AdminRoute = z.infer<typeof AdminRoute>;
|
|
75
|
+
/**
|
|
76
|
+
* An admin route as a capability declares it: a scoped route states its kind, and a route with no scope has none.
|
|
77
|
+
* `Capability.adminRoutes` takes this, so a scoped route without a kind does not compile.
|
|
78
|
+
*/
|
|
79
|
+
export type AdminRouteDeclaration = Omit<AdminRoute, "scope" | "kind"> & ({
|
|
80
|
+
scope: ControlPlaneScope;
|
|
81
|
+
kind: ScopeKind;
|
|
82
|
+
} | {
|
|
83
|
+
scope: null;
|
|
84
|
+
kind?: undefined;
|
|
85
|
+
});
|
|
45
86
|
/**
|
|
46
87
|
* One composed capability's declaration — everything about it that is the same for every caller.
|
|
47
88
|
*
|
|
@@ -64,6 +105,16 @@ export declare const CapabilityDeclaration: z.ZodObject<{
|
|
|
64
105
|
path: z.ZodString;
|
|
65
106
|
scope: z.ZodNullable<z.ZodString>;
|
|
66
107
|
summary: z.ZodString;
|
|
108
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
109
|
+
charges: "charges";
|
|
110
|
+
credentials: "credentials";
|
|
111
|
+
deletes: "deletes";
|
|
112
|
+
reads: "reads";
|
|
113
|
+
sends: "sends";
|
|
114
|
+
writes: "writes";
|
|
115
|
+
}>>;
|
|
116
|
+
operation: z.ZodOptional<z.ZodString>;
|
|
117
|
+
input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
67
118
|
}, z.core.$strip>>;
|
|
68
119
|
healthKeys: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
69
120
|
key: z.ZodString;
|
|
@@ -89,6 +140,14 @@ export declare const CapabilityDeclaration: z.ZodObject<{
|
|
|
89
140
|
summary: z.ZodString;
|
|
90
141
|
}, z.core.$strip>>>;
|
|
91
142
|
config: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
143
|
+
presenceRooms: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
144
|
+
kind: z.ZodString;
|
|
145
|
+
scope: z.ZodString;
|
|
146
|
+
activities: z.ZodArray<z.ZodString>;
|
|
147
|
+
maxParticipants: z.ZodNumber;
|
|
148
|
+
sessionTicketPath: z.ZodNullable<z.ZodString>;
|
|
149
|
+
socketPath: z.ZodString;
|
|
150
|
+
}, z.core.$strip>>>;
|
|
92
151
|
}, z.core.$strip>;
|
|
93
152
|
export type CapabilityDeclaration = z.infer<typeof CapabilityDeclaration>;
|
|
94
153
|
export declare const CapabilityDescriptor: z.ZodCodec<z.ZodObject<{
|
|
@@ -106,6 +165,16 @@ export declare const CapabilityDescriptor: z.ZodCodec<z.ZodObject<{
|
|
|
106
165
|
path: z.ZodString;
|
|
107
166
|
scope: z.ZodNullable<z.ZodString>;
|
|
108
167
|
summary: z.ZodString;
|
|
168
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
169
|
+
charges: "charges";
|
|
170
|
+
credentials: "credentials";
|
|
171
|
+
deletes: "deletes";
|
|
172
|
+
reads: "reads";
|
|
173
|
+
sends: "sends";
|
|
174
|
+
writes: "writes";
|
|
175
|
+
}>>;
|
|
176
|
+
operation: z.ZodOptional<z.ZodString>;
|
|
177
|
+
input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
109
178
|
}, z.core.$strip>>;
|
|
110
179
|
healthKeys: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
111
180
|
key: z.ZodString;
|
|
@@ -131,6 +200,14 @@ export declare const CapabilityDescriptor: z.ZodCodec<z.ZodObject<{
|
|
|
131
200
|
summary: z.ZodString;
|
|
132
201
|
}, z.core.$strip>>>;
|
|
133
202
|
config: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
203
|
+
presenceRooms: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
204
|
+
kind: z.ZodString;
|
|
205
|
+
scope: z.ZodString;
|
|
206
|
+
activities: z.ZodArray<z.ZodString>;
|
|
207
|
+
maxParticipants: z.ZodNumber;
|
|
208
|
+
sessionTicketPath: z.ZodNullable<z.ZodString>;
|
|
209
|
+
socketPath: z.ZodString;
|
|
210
|
+
}, z.core.$strip>>>;
|
|
134
211
|
health: z.ZodDefault<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>>;
|
|
135
212
|
healthUnavailable: z.ZodDefault<z.ZodBoolean>;
|
|
136
213
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -148,6 +225,16 @@ export declare const CapabilityDescriptor: z.ZodCodec<z.ZodObject<{
|
|
|
148
225
|
path: z.ZodString;
|
|
149
226
|
scope: z.ZodNullable<z.ZodString>;
|
|
150
227
|
summary: z.ZodString;
|
|
228
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
229
|
+
charges: "charges";
|
|
230
|
+
credentials: "credentials";
|
|
231
|
+
deletes: "deletes";
|
|
232
|
+
reads: "reads";
|
|
233
|
+
sends: "sends";
|
|
234
|
+
writes: "writes";
|
|
235
|
+
}>>;
|
|
236
|
+
operation: z.ZodOptional<z.ZodString>;
|
|
237
|
+
input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
151
238
|
}, z.core.$strip>>;
|
|
152
239
|
healthKeys: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
153
240
|
key: z.ZodString;
|
|
@@ -173,6 +260,14 @@ export declare const CapabilityDescriptor: z.ZodCodec<z.ZodObject<{
|
|
|
173
260
|
summary: z.ZodString;
|
|
174
261
|
}, z.core.$strip>>>;
|
|
175
262
|
config: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
263
|
+
presenceRooms: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
264
|
+
kind: z.ZodString;
|
|
265
|
+
scope: z.ZodString;
|
|
266
|
+
activities: z.ZodArray<z.ZodString>;
|
|
267
|
+
maxParticipants: z.ZodNumber;
|
|
268
|
+
sessionTicketPath: z.ZodNullable<z.ZodString>;
|
|
269
|
+
socketPath: z.ZodString;
|
|
270
|
+
}, z.core.$strip>>>;
|
|
176
271
|
health: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
177
272
|
state: z.ZodLiteral<"undeclared">;
|
|
178
273
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -193,7 +288,7 @@ export type CapabilityDescriptor = z.infer<typeof CapabilityDescriptor>;
|
|
|
193
288
|
* now — so a capability that changed its paths or its scopes reports the change directly, and a schema
|
|
194
289
|
* version would be a second source of truth to keep in sync with the first. That reasoning is unchanged.
|
|
195
290
|
*
|
|
196
|
-
* **And `capabilities[].version` travels with the package it belongs to
|
|
291
|
+
* **And `capabilities[].version` travels with the package it belongs to**. A version is only
|
|
197
292
|
* actionable joined against a release feed, a feed is keyed by package name, and a capability name is
|
|
198
293
|
* not one — `controlplane` ships inside `@pithy-sh/core`. Reporting the version alone left every client
|
|
199
294
|
* guessing the key, and the guess failed silently on the most frequently released package there is.
|
|
@@ -207,7 +302,7 @@ export type CapabilityDescriptor = z.infer<typeof CapabilityDescriptor>;
|
|
|
207
302
|
* we just fixed", and "does this project predate the capability a pane needs". Reporting only one leaves
|
|
208
303
|
* half the questions unanswerable, and they are the halves people actually ask.
|
|
209
304
|
*
|
|
210
|
-
* **And each entry carries a third thing: the configured facts that capability states
|
|
305
|
+
* **And each entry carries a third thing: the configured facts that capability states**. Knowing
|
|
211
306
|
* where a route is and which scope it needs is not always enough to call it — `POST
|
|
212
307
|
* {base}/entitlements/grant` names a holder, and whether this project's holders are people or
|
|
213
308
|
* organizations is a decision the adopter made in config. A client that cannot read the decision guesses
|
|
@@ -247,6 +342,38 @@ export declare const ControlPlaneManifest: z.ZodObject<{
|
|
|
247
342
|
liveVersions: z.ZodNumber;
|
|
248
343
|
}, z.core.$strip>>;
|
|
249
344
|
}, z.core.$strip>>;
|
|
345
|
+
retention: z.ZodOptional<z.ZodObject<{
|
|
346
|
+
purgeWindows: z.ZodArray<z.ZodObject<{
|
|
347
|
+
subject: z.ZodEnum<{
|
|
348
|
+
organization: "organization";
|
|
349
|
+
user: "user";
|
|
350
|
+
}>;
|
|
351
|
+
days: z.ZodNumber;
|
|
352
|
+
source: z.ZodEnum<{
|
|
353
|
+
capability: "capability";
|
|
354
|
+
floor: "floor";
|
|
355
|
+
policy: "policy";
|
|
356
|
+
}>;
|
|
357
|
+
}, z.core.$strip>>;
|
|
358
|
+
audit: z.ZodObject<{
|
|
359
|
+
defaultDays: z.ZodNullable<z.ZodNumber>;
|
|
360
|
+
floorDays: z.ZodNumber;
|
|
361
|
+
rules: z.ZodArray<z.ZodObject<{
|
|
362
|
+
prefix: z.ZodString;
|
|
363
|
+
days: z.ZodNumber;
|
|
364
|
+
source: z.ZodEnum<{
|
|
365
|
+
capability: "capability";
|
|
366
|
+
floor: "floor";
|
|
367
|
+
policy: "policy";
|
|
368
|
+
}>;
|
|
369
|
+
}, z.core.$strip>>;
|
|
370
|
+
floored: z.ZodArray<z.ZodString>;
|
|
371
|
+
}, z.core.$strip>;
|
|
372
|
+
classAges: z.ZodArray<z.ZodObject<{
|
|
373
|
+
class: z.ZodString;
|
|
374
|
+
days: z.ZodNumber;
|
|
375
|
+
}, z.core.$strip>>;
|
|
376
|
+
}, z.core.$strip>>;
|
|
250
377
|
capabilities: z.ZodArray<z.ZodCodec<z.ZodObject<{
|
|
251
378
|
name: z.ZodString;
|
|
252
379
|
version: z.ZodNullable<z.ZodString>;
|
|
@@ -262,6 +389,16 @@ export declare const ControlPlaneManifest: z.ZodObject<{
|
|
|
262
389
|
path: z.ZodString;
|
|
263
390
|
scope: z.ZodNullable<z.ZodString>;
|
|
264
391
|
summary: z.ZodString;
|
|
392
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
393
|
+
charges: "charges";
|
|
394
|
+
credentials: "credentials";
|
|
395
|
+
deletes: "deletes";
|
|
396
|
+
reads: "reads";
|
|
397
|
+
sends: "sends";
|
|
398
|
+
writes: "writes";
|
|
399
|
+
}>>;
|
|
400
|
+
operation: z.ZodOptional<z.ZodString>;
|
|
401
|
+
input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
265
402
|
}, z.core.$strip>>;
|
|
266
403
|
healthKeys: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
267
404
|
key: z.ZodString;
|
|
@@ -287,6 +424,14 @@ export declare const ControlPlaneManifest: z.ZodObject<{
|
|
|
287
424
|
summary: z.ZodString;
|
|
288
425
|
}, z.core.$strip>>>;
|
|
289
426
|
config: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
427
|
+
presenceRooms: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
428
|
+
kind: z.ZodString;
|
|
429
|
+
scope: z.ZodString;
|
|
430
|
+
activities: z.ZodArray<z.ZodString>;
|
|
431
|
+
maxParticipants: z.ZodNumber;
|
|
432
|
+
sessionTicketPath: z.ZodNullable<z.ZodString>;
|
|
433
|
+
socketPath: z.ZodString;
|
|
434
|
+
}, z.core.$strip>>>;
|
|
290
435
|
health: z.ZodDefault<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>>;
|
|
291
436
|
healthUnavailable: z.ZodDefault<z.ZodBoolean>;
|
|
292
437
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -304,6 +449,16 @@ export declare const ControlPlaneManifest: z.ZodObject<{
|
|
|
304
449
|
path: z.ZodString;
|
|
305
450
|
scope: z.ZodNullable<z.ZodString>;
|
|
306
451
|
summary: z.ZodString;
|
|
452
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
453
|
+
charges: "charges";
|
|
454
|
+
credentials: "credentials";
|
|
455
|
+
deletes: "deletes";
|
|
456
|
+
reads: "reads";
|
|
457
|
+
sends: "sends";
|
|
458
|
+
writes: "writes";
|
|
459
|
+
}>>;
|
|
460
|
+
operation: z.ZodOptional<z.ZodString>;
|
|
461
|
+
input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
307
462
|
}, z.core.$strip>>;
|
|
308
463
|
healthKeys: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
309
464
|
key: z.ZodString;
|
|
@@ -329,6 +484,14 @@ export declare const ControlPlaneManifest: z.ZodObject<{
|
|
|
329
484
|
summary: z.ZodString;
|
|
330
485
|
}, z.core.$strip>>>;
|
|
331
486
|
config: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
487
|
+
presenceRooms: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
488
|
+
kind: z.ZodString;
|
|
489
|
+
scope: z.ZodString;
|
|
490
|
+
activities: z.ZodArray<z.ZodString>;
|
|
491
|
+
maxParticipants: z.ZodNumber;
|
|
492
|
+
sessionTicketPath: z.ZodNullable<z.ZodString>;
|
|
493
|
+
socketPath: z.ZodString;
|
|
494
|
+
}, z.core.$strip>>>;
|
|
332
495
|
health: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
333
496
|
state: z.ZodLiteral<"undeclared">;
|
|
334
497
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adminRoute.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/discovery/adminRoute.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"adminRoute.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/discovery/adminRoute.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAInD;;;;;;;;;;;;;;;;;GAiBG;AAEH,+CAA+C;AAC/C,eAAO,MAAM,gBAAgB;;;;;;EAE8B,CAAC;AAC5D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,aAOgE,CAAC;AAC5F,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,wCAE+D,CAAC;AAC5F,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,oFAAoF;AACpF,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;iBA+BpB,CAAC;AACJ,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,CAAC,GACpE,CAAC;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAEtF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6D/B,CAAC;AACJ,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAiD1E,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAsC9B,CAAC;AACJ,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoCkE,CAAC;AACpG,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
|