@fourier-labs/harbour-contracts 0.9.0 → 0.11.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.
Files changed (41) hide show
  1. package/README.md +5 -0
  2. package/contract-release.json +31 -18
  3. package/dist/gen-ts/binding-classification-digest-helper.d.ts +11 -0
  4. package/dist/gen-ts/binding-classification-digest-helper.js +47 -0
  5. package/dist/gen-ts/binding-classification-subject.d.ts +56 -0
  6. package/dist/gen-ts/binding-classification-subject.js +3 -0
  7. package/dist/gen-ts/contract-release.d.ts +7 -7
  8. package/dist/gen-ts/contract-release.js +7 -7
  9. package/dist/gen-ts/deploy-request.d.ts +52 -0
  10. package/dist/gen-ts/deploy-response.d.ts +8 -0
  11. package/dist/gen-ts/index.d.ts +4 -0
  12. package/dist/gen-ts/index.js +4 -0
  13. package/dist/gen-ts/promotion-request.d.ts +54 -2
  14. package/dist/gen-ts/promotion-response.d.ts +8 -0
  15. package/dist/gen-ts/sanitized-binding-digest-helper.d.ts +7 -0
  16. package/dist/gen-ts/sanitized-binding-digest-helper.js +58 -0
  17. package/dist/gen-ts/sanitized-binding-evidence.d.ts +47 -0
  18. package/dist/gen-ts/sanitized-binding-evidence.js +3 -0
  19. package/fixtures/ai/adversarial-binding-classification-value.json +28 -0
  20. package/fixtures/ai/adversarial-sanitized-binding-relabel.json +36 -0
  21. package/fixtures/ai/valid-binding-classification-subject.json +50 -0
  22. package/fixtures/ai/valid-sanitized-binding-evidence-byo-source.json +36 -0
  23. package/fixtures/ai/valid-sanitized-binding-evidence.json +36 -0
  24. package/fixtures/ai/valid-source-revision-digest.json +6 -0
  25. package/gen-ts/binding-classification-digest-helper.ts +44 -0
  26. package/gen-ts/binding-classification-subject.ts +60 -0
  27. package/gen-ts/contract-release.ts +7 -7
  28. package/gen-ts/deploy-request.ts +51 -0
  29. package/gen-ts/deploy-response.ts +8 -0
  30. package/gen-ts/index.ts +4 -0
  31. package/gen-ts/promotion-request.ts +54 -2
  32. package/gen-ts/promotion-response.ts +8 -0
  33. package/gen-ts/sanitized-binding-digest-helper.ts +33 -0
  34. package/gen-ts/sanitized-binding-evidence.ts +51 -0
  35. package/package.json +1 -1
  36. package/schema/binding-classification-subject.schema.json +119 -0
  37. package/schema/deploy-request.schema.json +37 -2
  38. package/schema/deploy-response.schema.json +11 -1
  39. package/schema/promotion-request.schema.json +8 -4
  40. package/schema/promotion-response.schema.json +11 -1
  41. package/schema/sanitized-binding-evidence.schema.json +102 -0
@@ -0,0 +1,51 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ export type SanitizedBindingIdentifier = string;
5
+ export type SanitizedBindingSourceRef = string;
6
+ export type SanitizedBindingSourceRevision = string;
7
+ export type SanitizedBindingDigest = string;
8
+ export type SanitizedRuntimeBinding = {
9
+ kind: 'configuration' | 'secret';
10
+ /**
11
+ * Approved non-secret configuration literal. Provider-credential values are never approved or persisted here.
12
+ */
13
+ value?: string;
14
+ /**
15
+ * Value-free app secret, app-scoped minted data-source credential, or approved tenant-source credential reference. Integration and provider credential references are outside this contract.
16
+ */
17
+ secretRef?: string;
18
+ };
19
+ export type SanitizedBindingProviderId = 'openai' | 'anthropic' | 'gemini' | 'openrouter';
20
+
21
+ /**
22
+ * Immutable DCP allocation receipt and sanitized runtime-binding set for one exact allocated version and normalized source revision. DCP persists this evidence atomically with version admission before publish or provisioning. classificationDigest chains to the caller's pre-allocation BindingClassificationSubject; bindings contain only approved configuration, app secrets or approved data-source credential references and never an AI-mapping-owned provider credential.
23
+ */
24
+ export interface SanitizedBindingEvidence {
25
+ schema: 'harbour.sanitized-binding-evidence/1.0';
26
+ tenantId: SanitizedBindingIdentifier;
27
+ appId: SanitizedBindingIdentifier;
28
+ environment: 'preview' | 'prod';
29
+ versionId: string;
30
+ sourceRef: SanitizedBindingSourceRef;
31
+ sourceRevision: SanitizedBindingSourceRevision;
32
+ sourceRevisionDigest: SanitizedBindingDigest;
33
+ classificationDigest: SanitizedBindingDigest;
34
+ /**
35
+ * Exact approved runtime-binding set keyed by environment name. Object keys make duplicate bindings unrepresentable. A provider_credential kind and rejected binding metadata/value are unrepresentable.
36
+ */
37
+ bindings: {
38
+ [k: string]: SanitizedRuntimeBinding;
39
+ };
40
+ /**
41
+ * DCP-validated supported AI mapping requirements keyed by mapping ID. This records what later PLAN/provisioning milestones must enforce; it does not claim those later controls are already active.
42
+ */
43
+ requiredAiMappings: {
44
+ [k: string]: SanitizedBindingAiMapping;
45
+ };
46
+ sanitizedBindingDigest: SanitizedBindingDigest;
47
+ }
48
+ export interface SanitizedBindingAiMapping {
49
+ mappingVersion: string;
50
+ providerId: SanitizedBindingProviderId;
51
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fourier-labs/harbour-contracts",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Single source of truth for every data shape that crosses a boundary between Harbour components.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -0,0 +1,119 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/binding-classification-subject.schema.json",
4
+ "title": "BindingClassificationSubject",
5
+ "description": "Complete, immutable and value-free classification of the exact declared environment-binding names for one provider-neutral normalized source revision before DCP allocates a version. The caller never guesses a versionId. DCP validates this subject, allocates the sole versionId and chains classificationDigest into separately persisted SanitizedBindingEvidence; it must never relabel this source-bound evidence.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema", "status", "tenantId", "appId", "environment", "sourceRef", "sourceRevision", "sourceRevisionDigest", "classifierRevision", "classifications", "requestedAiMappings", "classificationDigest"],
9
+ "properties": {
10
+ "schema": { "type": "string", "const": "harbour.binding-classification-subject/1.0" },
11
+ "status": { "type": "string", "const": "COMPLETE", "description": "Partial or unavailable classification cannot be represented as an admissible subject." },
12
+ "tenantId": { "$ref": "#/definitions/BindingClassificationIdentifier" },
13
+ "appId": { "$ref": "#/definitions/BindingClassificationIdentifier" },
14
+ "environment": { "type": "string", "enum": ["preview", "prod"] },
15
+ "sourceRef": { "$ref": "#/definitions/BindingClassificationSourceRef" },
16
+ "sourceRevision": { "$ref": "#/definitions/BindingClassificationSourceRevision" },
17
+ "sourceRevisionDigest": { "$ref": "#/definitions/BindingClassificationDigest" },
18
+ "classifierRevision": { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[A-Za-z0-9][A-Za-z0-9._/-]*$" },
19
+ "classifications": {
20
+ "description": "Exact classification set keyed by every declared environment-binding name. Object keys make missing duplication tricks and two classifications for one name unrepresentable. Literal values and secret references are intentionally unrepresentable. DCP additionally requires these keys to equal the names in the request's environmentBindings before allocation.",
21
+ "type": "object",
22
+ "minProperties": 0,
23
+ "maxProperties": 512,
24
+ "propertyNames": { "$ref": "#/definitions/BindingClassificationName" },
25
+ "additionalProperties": { "$ref": "#/definitions/BindingClassificationEntry" }
26
+ },
27
+ "requestedAiMappings": {
28
+ "description": "Exact set keyed by requested AI mapping ID of supported mappings intended to own provider-client credentials for this source revision. This is pre-PLAN request evidence, not a claim that enforcement already exists. Provider-credential classifications reference one of these exact mapping IDs.",
29
+ "type": "object",
30
+ "minProperties": 0,
31
+ "maxProperties": 64,
32
+ "propertyNames": { "$ref": "#/definitions/BindingClassificationIdentifier" },
33
+ "additionalProperties": { "$ref": "#/definitions/BindingClassificationAiMapping" }
34
+ },
35
+ "classificationDigest": { "$ref": "#/definitions/BindingClassificationDigest" }
36
+ },
37
+ "definitions": {
38
+ "BindingClassificationIdentifier": {
39
+ "type": "string",
40
+ "minLength": 1,
41
+ "maxLength": 128,
42
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]*$"
43
+ },
44
+ "BindingClassificationName": {
45
+ "type": "string",
46
+ "pattern": "^[A-Z][A-Z0-9_]{0,127}$"
47
+ },
48
+ "BindingClassificationDigest": {
49
+ "type": "string",
50
+ "pattern": "^sha256:[0-9a-f]{64}$"
51
+ },
52
+ "BindingClassificationSourceRef": {
53
+ "description": "Provider-neutral, normalized and credential-free source repository reference. Current GitHub HTTPS references and future SCM adapters use the same safe hierarchical grammar; userinfo, ports, percent encoding, query strings and fragments are unrepresentable.",
54
+ "type": "string",
55
+ "maxLength": 1024,
56
+ "pattern": "^[a-z][a-z0-9+.-]*://[A-Za-z0-9][A-Za-z0-9.-]{0,127}(?:/[A-Za-z0-9][A-Za-z0-9._~-]{0,255})+$"
57
+ },
58
+ "BindingClassificationSourceRevision": {
59
+ "description": "Provider-neutral exact revision identifier expressed as algorithm:value. Current Git adapters use git-sha1:<40 lowercase hex>; another SCM or Git hash algorithm does not require a shape change.",
60
+ "type": "string",
61
+ "minLength": 9,
62
+ "maxLength": 320,
63
+ "pattern": "^[a-z][a-z0-9._-]{0,31}:[A-Za-z0-9][A-Za-z0-9._-]{6,287}$"
64
+ },
65
+ "BindingClassificationProviderId": {
66
+ "type": "string",
67
+ "enum": ["openai", "anthropic", "gemini", "openrouter"]
68
+ },
69
+ "BindingClassificationCredentialCategory": {
70
+ "description": "Closed current credential category. Add a reviewed contract version before supporting another credential mechanism.",
71
+ "type": "string",
72
+ "enum": ["api_key"]
73
+ },
74
+ "BindingClassificationAiMapping": {
75
+ "type": "object",
76
+ "additionalProperties": false,
77
+ "required": ["mappingVersion", "providerId"],
78
+ "properties": {
79
+ "mappingVersion": { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$" },
80
+ "providerId": { "$ref": "#/definitions/BindingClassificationProviderId" }
81
+ }
82
+ },
83
+ "BindingClassificationEntry": {
84
+ "type": "object",
85
+ "additionalProperties": false,
86
+ "required": ["kind", "confidence"],
87
+ "properties": {
88
+ "kind": { "type": "string", "enum": ["configuration", "secret", "provider_credential"] },
89
+ "confidence": { "type": "string", "enum": ["high", "low"] },
90
+ "providerId": { "$ref": "#/definitions/BindingClassificationProviderId" },
91
+ "category": { "$ref": "#/definitions/BindingClassificationCredentialCategory" },
92
+ "requestedAiMappingId": { "$ref": "#/definitions/BindingClassificationIdentifier" }
93
+ },
94
+ "allOf": [
95
+ {
96
+ "if": { "properties": { "kind": { "const": "provider_credential" } }, "required": ["kind"] },
97
+ "then": { "required": ["providerId", "category", "requestedAiMappingId"] },
98
+ "else": { "not": { "anyOf": [{ "required": ["providerId"] }, { "required": ["category"] }, { "required": ["requestedAiMappingId"] }] } }
99
+ }
100
+ ]
101
+ }
102
+ },
103
+ "x-digest": {
104
+ "field": "classificationDigest",
105
+ "algorithm": "sha256",
106
+ "encoding": "utf8",
107
+ "canonicalization": "harbour-length-prefixed-records/1.0",
108
+ "excludedFields": ["classificationDigest"],
109
+ "semanticPreconditions": ["classifications object keys exactly equal the declared environmentBindings names", "each provider_credential entry references a present requestedAiMappings key with the same providerId"]
110
+ },
111
+ "x-source-revision-digest": {
112
+ "field": "sourceRevisionDigest",
113
+ "domain": "harbour.source-revision/1.0",
114
+ "algorithm": "sha256",
115
+ "encoding": "utf8",
116
+ "canonicalization": "harbour-length-prefixed-records/1.0",
117
+ "inputs": ["sourceRef", "sourceRevision"]
118
+ }
119
+ }
@@ -5,7 +5,7 @@
5
5
  "description": "JSON body for POST /v1/tenants/{tenant}/apps/{app}/deployments. tenant and app come from path parameters and are not body fields. The preview MVP requires environment=preview. Every accepted POST is a new deployment intent; idempotencyKey is retained only as request correlation.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
- "required": ["source", "environment", "branch", "commit"],
8
+ "required": ["source", "environment", "branch", "commit", "bindingClassification"],
9
9
  "properties": {
10
10
  "source": {
11
11
  "$ref": "#/definitions/DeployRequestSource"
@@ -33,6 +33,10 @@
33
33
  },
34
34
  "environmentBindings": {
35
35
  "$ref": "#/definitions/DeployRequestEnvironmentBindings"
36
+ },
37
+ "bindingClassification": {
38
+ "$ref": "binding-classification-subject.schema.json",
39
+ "description": "Complete pre-allocation, value-free binding-name classification. DCP verifies its tenant/app/environment/source identity and exact name coverage before minting the versionId."
36
40
  }
37
41
  },
38
42
  "definitions": {
@@ -107,7 +111,21 @@
107
111
  "environment": "preview",
108
112
  "branch": "preview-deployment",
109
113
  "commit": "abc1234567890abcdef1234567890abcdef12345",
110
- "idempotencyKey": "deploy-attempt-123"
114
+ "idempotencyKey": "deploy-attempt-123",
115
+ "bindingClassification": {
116
+ "schema": "harbour.binding-classification-subject/1.0",
117
+ "status": "COMPLETE",
118
+ "tenantId": "fourier",
119
+ "appId": "focus-list",
120
+ "environment": "preview",
121
+ "sourceRef": "https://github.com/Fourier-Labs-AI/focus-list",
122
+ "sourceRevision": "git-sha1:abc1234567890abcdef1234567890abcdef12345",
123
+ "sourceRevisionDigest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
124
+ "classifierRevision": "secret-name-classifier-2.0",
125
+ "classifications": {},
126
+ "requestedAiMappings": {},
127
+ "classificationDigest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
128
+ }
111
129
  },
112
130
  {
113
131
  "source": {
@@ -122,6 +140,23 @@
122
140
  { "name": "STRIPE_API_KEY", "secretRef": "secretref://tenants/fourier/apps/reports/STRIPE_API_KEY" },
123
141
  { "name": "LOG_LEVEL", "value": "debug" }
124
142
  ]
143
+ },
144
+ "bindingClassification": {
145
+ "schema": "harbour.binding-classification-subject/1.0",
146
+ "status": "COMPLETE",
147
+ "tenantId": "fourier",
148
+ "appId": "focus-list",
149
+ "environment": "preview",
150
+ "sourceRef": "https://github.com/Fourier-Labs-AI/focus-list",
151
+ "sourceRevision": "git-sha1:abc1234567890abcdef1234567890abcdef12345",
152
+ "sourceRevisionDigest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
153
+ "classifierRevision": "secret-name-classifier-2.0",
154
+ "classifications": {
155
+ "LOG_LEVEL": { "kind": "configuration", "confidence": "high" },
156
+ "STRIPE_API_KEY": { "kind": "secret", "confidence": "high" }
157
+ },
158
+ "requestedAiMappings": {},
159
+ "classificationDigest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
125
160
  }
126
161
  }
127
162
  ]
@@ -5,7 +5,7 @@
5
5
  "description": "202 Accepted body returned by POST /v1/tenants/{tenant}/apps/{app}/deployments only after the authoritative QUEUED version exists and its version-qualified SQS message is accepted.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
- "required": ["requestId", "versionId", "tenant", "app", "environment", "status", "acceptedAt", "statusUrl"],
8
+ "required": ["requestId", "versionId", "tenant", "app", "environment", "status", "classificationDigest", "sanitizedBindingDigest", "acceptedAt", "statusUrl"],
9
9
  "properties": {
10
10
  "requestId": {
11
11
  "type": "string",
@@ -36,6 +36,16 @@
36
36
  "type": "string",
37
37
  "enum": ["QUEUED"]
38
38
  },
39
+ "classificationDigest": {
40
+ "type": "string",
41
+ "pattern": "^sha256:[0-9a-f]{64}$",
42
+ "description": "Digest of the validated pre-allocation BindingClassificationSubject."
43
+ },
44
+ "sanitizedBindingDigest": {
45
+ "type": "string",
46
+ "pattern": "^sha256:[0-9a-f]{64}$",
47
+ "description": "Digest of the exact version-bound SanitizedBindingEvidence persisted atomically before this response."
48
+ },
39
49
  "acceptedAt": {
40
50
  "type": "string",
41
51
  "format": "date-time"
@@ -5,7 +5,7 @@
5
5
  "description": "JSON body for POST /v1/tenants/{tenant}/apps/{app}/promotions. Repository identity is loaded from stored application and preview records, not supplied by the caller.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
- "required": ["previewVersionId", "commit", "idempotencyKey"],
8
+ "required": ["previewVersionId", "commit", "idempotencyKey", "bindingClassification"],
9
9
  "properties": {
10
10
  "previewVersionId": {
11
11
  "type": "string",
@@ -24,6 +24,10 @@
24
24
  },
25
25
  "environmentBindings": {
26
26
  "$ref": "#/definitions/PromotionRequestEnvironmentBindings"
27
+ },
28
+ "bindingClassification": {
29
+ "$ref": "binding-classification-subject.schema.json",
30
+ "description": "Complete pre-allocation, value-free classification for the exact production source revision and declared production binding names. It intentionally has no production versionId; DCP allocates that identity atomically."
27
31
  }
28
32
  },
29
33
  "definitions": {
@@ -54,11 +58,11 @@
54
58
  },
55
59
  "secretRef": {
56
60
  "type": "string",
57
- "pattern": "^secretref://tenant/[A-Za-z0-9._-]+/app/[A-Za-z0-9._-]+/secret/[A-Z][A-Z0-9_]{0,127}$",
58
- "description": "Governance reference the deployment plane resolves. The value never appears in the request, in this schema, or in any record derived from it."
61
+ "pattern": "^secretref://tenant/[A-Za-z0-9._-]+/(?:app/[A-Za-z0-9._-]+/(?:secret/[A-Z][A-Z0-9_]{0,127}|source/[a-z0-9][a-z0-9._-]{0,63})|source/[a-z0-9][a-z0-9._-]{0,63})$",
62
+ "description": "Governance app secret, app-scoped minted data-source credential, or approved tenant-source credential reference the deployment plane resolves. The value never appears in the request, in this schema, or in any record derived from it."
59
63
  }
60
64
  },
61
- "description": "One secret binding. Unlike the preview counterpart there is no `value` member and `secretRef` is required, so a plain literal is unrepresentable on the promotion path rather than merely refused by the handler. The secretRef pattern admits only the app-secret shape, which is what keeps a promotion from naming a tenant integration credential."
65
+ "description": "One secret binding. Unlike the preview counterpart there is no `value` member and `secretRef` is required, so a plain literal is unrepresentable on the promotion path rather than merely refused by the handler. The secretRef pattern admits only app secrets, app-scoped minted data-source credentials and tenant-scoped source credentials from an approved BYO grant; integration and provider credentials remain unrepresentable."
62
66
  }
63
67
  }
64
68
  }
@@ -5,7 +5,7 @@
5
5
  "description": "202 Accepted body returned after the production version and its durable idempotency binding exist.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
- "required": ["idempotencyKey", "versionId", "previewVersionId", "tenant", "app", "environment", "status", "acceptedAt", "statusUrl"],
8
+ "required": ["idempotencyKey", "versionId", "previewVersionId", "tenant", "app", "environment", "status", "classificationDigest", "sanitizedBindingDigest", "acceptedAt", "statusUrl"],
9
9
  "properties": {
10
10
  "idempotencyKey": {
11
11
  "type": "string",
@@ -38,6 +38,16 @@
38
38
  "type": "string",
39
39
  "enum": ["QUEUED"]
40
40
  },
41
+ "classificationDigest": {
42
+ "type": "string",
43
+ "pattern": "^sha256:[0-9a-f]{64}$",
44
+ "description": "Digest of the validated production BindingClassificationSubject."
45
+ },
46
+ "sanitizedBindingDigest": {
47
+ "type": "string",
48
+ "pattern": "^sha256:[0-9a-f]{64}$",
49
+ "description": "Digest of the exact production-version SanitizedBindingEvidence persisted atomically before this response."
50
+ },
41
51
  "acceptedAt": {
42
52
  "type": "string",
43
53
  "format": "date-time"
@@ -0,0 +1,102 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/sanitized-binding-evidence.schema.json",
4
+ "title": "SanitizedBindingEvidence",
5
+ "description": "Immutable DCP allocation receipt and sanitized runtime-binding set for one exact allocated version and normalized source revision. DCP persists this evidence atomically with version admission before publish or provisioning. classificationDigest chains to the caller's pre-allocation BindingClassificationSubject; bindings contain only approved configuration, app secrets or approved data-source credential references and never an AI-mapping-owned provider credential.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema", "tenantId", "appId", "environment", "versionId", "sourceRef", "sourceRevision", "sourceRevisionDigest", "classificationDigest", "bindings", "requiredAiMappings", "sanitizedBindingDigest"],
9
+ "properties": {
10
+ "schema": { "type": "string", "const": "harbour.sanitized-binding-evidence/1.0" },
11
+ "tenantId": { "$ref": "#/definitions/SanitizedBindingIdentifier" },
12
+ "appId": { "$ref": "#/definitions/SanitizedBindingIdentifier" },
13
+ "environment": { "type": "string", "enum": ["preview", "prod"] },
14
+ "versionId": { "type": "string", "minLength": 7, "maxLength": 320, "pattern": "^[^#]+#[^#]+#[^#]+#[0-9]{6,}$" },
15
+ "sourceRef": { "$ref": "#/definitions/SanitizedBindingSourceRef" },
16
+ "sourceRevision": { "$ref": "#/definitions/SanitizedBindingSourceRevision" },
17
+ "sourceRevisionDigest": { "$ref": "#/definitions/SanitizedBindingDigest" },
18
+ "classificationDigest": { "$ref": "#/definitions/SanitizedBindingDigest" },
19
+ "bindings": {
20
+ "description": "Exact approved runtime-binding set keyed by environment name. Object keys make duplicate bindings unrepresentable. A provider_credential kind and rejected binding metadata/value are unrepresentable.",
21
+ "type": "object",
22
+ "minProperties": 0,
23
+ "maxProperties": 512,
24
+ "propertyNames": { "$ref": "#/definitions/SanitizedBindingName" },
25
+ "additionalProperties": { "$ref": "#/definitions/SanitizedRuntimeBinding" }
26
+ },
27
+ "requiredAiMappings": {
28
+ "description": "DCP-validated supported AI mapping requirements keyed by mapping ID. This records what later PLAN/provisioning milestones must enforce; it does not claim those later controls are already active.",
29
+ "type": "object",
30
+ "minProperties": 0,
31
+ "maxProperties": 64,
32
+ "propertyNames": { "$ref": "#/definitions/SanitizedBindingIdentifier" },
33
+ "additionalProperties": { "$ref": "#/definitions/SanitizedBindingAiMapping" }
34
+ },
35
+ "sanitizedBindingDigest": { "$ref": "#/definitions/SanitizedBindingDigest" }
36
+ },
37
+ "definitions": {
38
+ "SanitizedBindingIdentifier": {
39
+ "type": "string",
40
+ "minLength": 1,
41
+ "maxLength": 128,
42
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]*$"
43
+ },
44
+ "SanitizedBindingName": {
45
+ "type": "string",
46
+ "pattern": "^[A-Z][A-Z0-9_]{0,127}$"
47
+ },
48
+ "SanitizedBindingDigest": {
49
+ "type": "string",
50
+ "pattern": "^sha256:[0-9a-f]{64}$"
51
+ },
52
+ "SanitizedBindingSourceRef": {
53
+ "type": "string",
54
+ "maxLength": 1024,
55
+ "pattern": "^[a-z][a-z0-9+.-]*://[A-Za-z0-9][A-Za-z0-9.-]{0,127}(?:/[A-Za-z0-9][A-Za-z0-9._~-]{0,255})+$"
56
+ },
57
+ "SanitizedBindingSourceRevision": {
58
+ "type": "string",
59
+ "minLength": 9,
60
+ "maxLength": 320,
61
+ "pattern": "^[a-z][a-z0-9._-]{0,31}:[A-Za-z0-9][A-Za-z0-9._-]{6,287}$"
62
+ },
63
+ "SanitizedBindingProviderId": {
64
+ "type": "string",
65
+ "enum": ["openai", "anthropic", "gemini", "openrouter"]
66
+ },
67
+ "SanitizedBindingAiMapping": {
68
+ "type": "object",
69
+ "additionalProperties": false,
70
+ "required": ["mappingVersion", "providerId"],
71
+ "properties": {
72
+ "mappingVersion": { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$" },
73
+ "providerId": { "$ref": "#/definitions/SanitizedBindingProviderId" }
74
+ }
75
+ },
76
+ "SanitizedRuntimeBinding": {
77
+ "type": "object",
78
+ "additionalProperties": false,
79
+ "required": ["kind"],
80
+ "properties": {
81
+ "kind": { "type": "string", "enum": ["configuration", "secret"] },
82
+ "value": { "type": "string", "minLength": 1, "maxLength": 4096, "description": "Approved non-secret configuration literal. Provider-credential values are never approved or persisted here." },
83
+ "secretRef": { "type": "string", "pattern": "^secretref://tenant/[A-Za-z0-9._-]+/(?:app/[A-Za-z0-9._-]+/(?:secret/[A-Z][A-Z0-9_]{0,127}|source/[a-z0-9][a-z0-9._-]{0,63})|source/[a-z0-9][a-z0-9._-]{0,63})$", "description": "Value-free app secret, app-scoped minted data-source credential, or approved tenant-source credential reference. Integration and provider credential references are outside this contract." }
84
+ },
85
+ "allOf": [
86
+ {
87
+ "if": { "properties": { "kind": { "const": "configuration" } }, "required": ["kind"] },
88
+ "then": { "required": ["value"], "not": { "required": ["secretRef"] } },
89
+ "else": { "required": ["secretRef"], "not": { "required": ["value"] } }
90
+ }
91
+ ]
92
+ }
93
+ },
94
+ "x-digest": {
95
+ "field": "sanitizedBindingDigest",
96
+ "algorithm": "sha256",
97
+ "encoding": "utf8",
98
+ "canonicalization": "harbour-length-prefixed-records/1.0",
99
+ "excludedFields": ["sanitizedBindingDigest"],
100
+ "semanticPreconditions": ["tenantId, appId, environment, sourceRef, sourceRevision and sourceRevisionDigest exactly equal the validated classification subject", "classificationDigest exactly equals the validated classification subject digest", "requiredAiMappings exactly equals the validated subject requestedAiMappings", "versionId is the sole DCP-allocated version admitted atomically with this evidence", "binding keys are exactly the classification keys not classified as requested AI-mapping-owned provider_credential"]
101
+ }
102
+ }