@fourier-labs/harbour-contracts 0.5.0 → 0.9.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 (121) hide show
  1. package/README.md +44 -4
  2. package/contract-release.json +134 -0
  3. package/dist/gen-ts/ai-budget-ledger-event.d.ts +21 -0
  4. package/dist/gen-ts/ai-budget-ledger-event.js +3 -0
  5. package/dist/gen-ts/ai-classification-stamp.d.ts +25 -0
  6. package/dist/gen-ts/ai-classification-stamp.js +3 -0
  7. package/dist/gen-ts/ai-composite-policy-digest-helper.d.ts +5 -0
  8. package/dist/gen-ts/ai-composite-policy-digest-helper.js +13 -0
  9. package/dist/gen-ts/ai-composite-policy-digest.d.ts +12 -0
  10. package/dist/gen-ts/ai-composite-policy-digest.js +3 -0
  11. package/dist/gen-ts/ai-deployment-gate-decision.d.ts +27 -0
  12. package/dist/gen-ts/ai-deployment-gate-decision.js +3 -0
  13. package/dist/gen-ts/ai-gateway-key-lifecycle-event.d.ts +49 -0
  14. package/dist/gen-ts/ai-gateway-key-lifecycle-event.js +16 -0
  15. package/dist/gen-ts/ai-governance-decision.d.ts +14 -2
  16. package/dist/gen-ts/ai-metering-policy.d.ts +16 -0
  17. package/dist/gen-ts/ai-metering-policy.js +3 -0
  18. package/dist/gen-ts/ai-policy-snapshot.d.ts +5 -269
  19. package/dist/gen-ts/ai-pricing-revision.d.ts +29 -0
  20. package/dist/gen-ts/ai-pricing-revision.js +3 -0
  21. package/dist/gen-ts/ai-runtime-binding.d.ts +14 -0
  22. package/dist/gen-ts/ai-runtime-binding.js +3 -0
  23. package/dist/gen-ts/ai-runtime-contract.d.ts +16 -1
  24. package/dist/gen-ts/ai-trace-event-v2.d.ts +31 -0
  25. package/dist/gen-ts/ai-trace-event-v2.js +3 -0
  26. package/dist/gen-ts/ai-transform-event.d.ts +2 -21
  27. package/dist/gen-ts/ai-transform-result.d.ts +1 -1
  28. package/dist/gen-ts/ai-verification-evidence.d.ts +2 -1
  29. package/dist/gen-ts/contract-release.d.ts +7 -0
  30. package/dist/gen-ts/contract-release.js +9 -0
  31. package/dist/gen-ts/data-grant.d.ts +1 -1
  32. package/dist/gen-ts/error-category-registry.d.ts +5 -0
  33. package/dist/gen-ts/error-category-registry.js +10 -0
  34. package/dist/gen-ts/index.d.ts +12 -0
  35. package/dist/gen-ts/index.js +12 -0
  36. package/dist/gen-ts/kvs-routing-entry.d.ts +19 -5
  37. package/dist/gen-ts/promotion-request.d.ts +23 -0
  38. package/dist/gen-ts/transform-report.d.ts +102 -0
  39. package/dist/gen-ts/transform-report.js +3 -0
  40. package/fixtures/ai/valid-blocked-trace-event.json +31 -0
  41. package/fixtures/ai/valid-budget-ledger-event.json +15 -0
  42. package/fixtures/ai/valid-classification-stamp.json +19 -0
  43. package/fixtures/ai/valid-composite-policy-digest.json +11 -0
  44. package/fixtures/ai/valid-deployment-gate-decision.json +18 -0
  45. package/fixtures/ai/valid-failed-trace-event.json +50 -0
  46. package/fixtures/ai/valid-gateway-key-lifecycle-event.json +16 -0
  47. package/fixtures/ai/valid-gateway-key-lookup.json +14 -0
  48. package/fixtures/ai/valid-governance-decision.json +53 -0
  49. package/fixtures/ai/valid-inventory.json +30 -0
  50. package/fixtures/ai/valid-metering-policy.json +8 -0
  51. package/fixtures/ai/valid-policy-snapshot.json +117 -0
  52. package/fixtures/ai/valid-pricing-revision.json +14 -0
  53. package/fixtures/ai/valid-runtime-binding.json +7 -0
  54. package/fixtures/ai/valid-runtime-contract.json +24 -0
  55. package/fixtures/ai/valid-trace-event-v2.json +24 -0
  56. package/fixtures/ai/valid-trace-event.json +41 -0
  57. package/fixtures/ai/valid-transform-event.json +32 -0
  58. package/fixtures/ai/valid-transform-result.json +23 -0
  59. package/fixtures/ai/valid-verification-evidence.json +33 -0
  60. package/gen-ts/ai-budget-ledger-event.ts +25 -0
  61. package/gen-ts/ai-callsite-inventory.ts +64 -0
  62. package/gen-ts/ai-classification-stamp.ts +28 -0
  63. package/gen-ts/ai-composite-policy-digest-helper.ts +18 -0
  64. package/gen-ts/ai-composite-policy-digest.ts +16 -0
  65. package/gen-ts/ai-deployment-gate-decision.ts +30 -0
  66. package/gen-ts/ai-gateway-key-lifecycle-event.ts +81 -0
  67. package/gen-ts/ai-gateway-key-lookup.ts +23 -0
  68. package/gen-ts/ai-gateway-key-token.ts +7 -0
  69. package/gen-ts/ai-governance-decision.ts +90 -0
  70. package/gen-ts/ai-metering-policy.ts +20 -0
  71. package/gen-ts/ai-policy-snapshot.ts +169 -0
  72. package/gen-ts/ai-pricing-revision.ts +33 -0
  73. package/gen-ts/ai-runtime-binding.ts +18 -0
  74. package/gen-ts/ai-runtime-contract.ts +46 -0
  75. package/gen-ts/ai-trace-event-v2.ts +35 -0
  76. package/gen-ts/ai-trace-event.ts +72 -0
  77. package/gen-ts/ai-transform-event.ts +51 -0
  78. package/gen-ts/ai-transform-result.ts +36 -0
  79. package/gen-ts/ai-verification-evidence.ts +54 -0
  80. package/gen-ts/api-error-response.ts +16 -0
  81. package/gen-ts/artifact-contract.ts +81 -0
  82. package/gen-ts/contract-release.ts +10 -0
  83. package/gen-ts/data-dependency.ts +46 -0
  84. package/gen-ts/data-grant.ts +71 -0
  85. package/gen-ts/data-source.ts +78 -0
  86. package/gen-ts/deploy-request.ts +66 -0
  87. package/gen-ts/deploy-response.ts +25 -0
  88. package/gen-ts/deployment-environment.ts +7 -0
  89. package/gen-ts/deployment-source.ts +16 -0
  90. package/gen-ts/deployment-status-failure.ts +12 -0
  91. package/gen-ts/deployment-status-request.ts +11 -0
  92. package/gen-ts/deployment-status-response.ts +39 -0
  93. package/gen-ts/env-bindings.ts +64 -0
  94. package/gen-ts/error-category-registry.ts +108 -0
  95. package/gen-ts/error-category.ts +7 -0
  96. package/gen-ts/error-envelope.ts +49 -0
  97. package/gen-ts/index.ts +47 -0
  98. package/gen-ts/kvs-routing-entry.ts +30 -0
  99. package/gen-ts/promotion-request.ts +43 -0
  100. package/gen-ts/promotion-response.ts +20 -0
  101. package/gen-ts/secret-reference.ts +40 -0
  102. package/gen-ts/transform-report.ts +105 -0
  103. package/gen-ts/version-state-machine.ts +52 -0
  104. package/gen-ts/version-state.ts +19 -0
  105. package/package.json +8 -4
  106. package/schema/ai-budget-ledger-event.schema.json +29 -0
  107. package/schema/ai-classification-stamp.schema.json +40 -0
  108. package/schema/ai-composite-policy-digest.schema.json +27 -0
  109. package/schema/ai-deployment-gate-decision.schema.json +38 -0
  110. package/schema/ai-gateway-key-lifecycle-event.schema.json +61 -0
  111. package/schema/ai-governance-decision.schema.json +23 -2
  112. package/schema/ai-metering-policy.schema.json +18 -0
  113. package/schema/ai-pricing-revision.schema.json +38 -0
  114. package/schema/ai-runtime-binding.schema.json +26 -0
  115. package/schema/ai-runtime-contract.schema.json +24 -2
  116. package/schema/ai-trace-event-v2.schema.json +41 -0
  117. package/schema/ai-verification-evidence.schema.json +1 -0
  118. package/schema/error-category.enum.json +6 -1
  119. package/schema/kvs-routing-entry.schema.json +28 -8
  120. package/schema/promotion-request.schema.json +38 -0
  121. package/schema/transform-report.schema.json +204 -0
@@ -0,0 +1,43 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ /**
5
+ * 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
+ */
7
+ export interface PromotionRequest {
8
+ /**
9
+ * Immutable preview version selected by the caller. The API also verifies that its tenant and app match the route.
10
+ */
11
+ previewVersionId: string;
12
+ /**
13
+ * Current commit at the configured production branch head. Its root Git tree must equal the preview tree.
14
+ */
15
+ commit: string;
16
+ /**
17
+ * Durable caller-generated key. Identical replays return the original production version; reuse with a changed request is rejected.
18
+ */
19
+ idempotencyKey: string;
20
+ environmentBindings?: PromotionRequestEnvironmentBindings;
21
+ }
22
+ /**
23
+ * Optional secret bindings for the production version. Additive: promotions without it remain valid, and a deployment plane that ignores the field behaves exactly as before. Named environmentBindings to match deploy-request.schema.json, whose preview counterpart this mirrors.
24
+ */
25
+ export interface PromotionRequestEnvironmentBindings {
26
+ /**
27
+ * Secret bindings for the production version. An empty array is meaningful: it says the caller looked and the app declares no secrets.
28
+ */
29
+ bindings: PromotionRequestSecretBinding[];
30
+ }
31
+ /**
32
+ * 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.
33
+ */
34
+ export interface PromotionRequestSecretBinding {
35
+ /**
36
+ * Environment-variable name the app reads.
37
+ */
38
+ name: string;
39
+ /**
40
+ * Governance reference the deployment plane resolves. The value never appears in the request, in this schema, or in any record derived from it.
41
+ */
42
+ secretRef: string;
43
+ }
@@ -0,0 +1,20 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ /**
5
+ * 202 Accepted body returned after the production version and its durable idempotency binding exist.
6
+ */
7
+ export interface PromotionResponse {
8
+ idempotencyKey: string;
9
+ /**
10
+ * Immutable identity of the admitted production version.
11
+ */
12
+ versionId: string;
13
+ previewVersionId: string;
14
+ tenant: string;
15
+ app: string;
16
+ environment: 'prod';
17
+ status: 'QUEUED';
18
+ acceptedAt: string;
19
+ statusUrl: string;
20
+ }
@@ -0,0 +1,40 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ /**
5
+ * app = shared by the whole app; personal = owned by a single builder (ownerBuilderId).
6
+ */
7
+ export type SecretReferenceScope = 'app' | 'personal';
8
+ /**
9
+ * UNSET slots block promotion until a human sets a value (secrets.unresolvable).
10
+ */
11
+ export type SecretReferenceStatus = 'SET' | 'UNSET';
12
+ /**
13
+ * builder = created by a builder; minted = created by the platform (e.g. a data-grant credential).
14
+ */
15
+ export type SecretReferenceOrigin = 'builder' | 'minted';
16
+
17
+ /**
18
+ * Secret-Manager MVP: a named secret slot an app depends on. Only the reference and its lifecycle cross this boundary — the secret VALUE never appears in any contract. scope=app secrets are shared by the app; scope=personal secrets belong to one builder (ownerBuilderId). origin records whether a builder created the slot or the platform minted it (e.g. for a data grant).
19
+ */
20
+ export interface SecretReference {
21
+ /**
22
+ * App the secret slot belongs to.
23
+ */
24
+ appId: string;
25
+ /**
26
+ * Slot name as the app references it (typically the environment-variable name).
27
+ */
28
+ name: string;
29
+ scope: SecretReferenceScope;
30
+ /**
31
+ * Owning builder; present only when scope is personal.
32
+ */
33
+ ownerBuilderId?: string;
34
+ /**
35
+ * secretref URI of the stored value. The value itself never crosses this boundary.
36
+ */
37
+ secretRef: string;
38
+ status: SecretReferenceStatus;
39
+ origin: SecretReferenceOrigin;
40
+ }
@@ -0,0 +1,105 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ /**
5
+ * Structured output an agent must return from one Harbour transformation attempt. Handed to the agent as its output schema, then decoded and semantically re-validated by the control plane, which remains the authority. The enum on applied_playbooks.items is the one dynamic part: the adapter injects the names of the playbooks supplied for that invocation before writing the document, so the model is structurally prevented from naming a playbook it was not given. Application-secret handling was removed platform-wide, so this document carries no redactions and no secrets fields.
6
+ */
7
+ export interface TransformReport {
8
+ /**
9
+ * @minItems 1
10
+ */
11
+ applied_playbooks: string[];
12
+ static_rebuild_required: boolean;
13
+ analysis: {
14
+ deployable: boolean;
15
+ needsTransformation: boolean;
16
+ /**
17
+ * @maxItems 64
18
+ */
19
+ blockers: string[];
20
+ /**
21
+ * @minItems 1
22
+ * @maxItems 1024
23
+ */
24
+ surfaces: string[];
25
+ /**
26
+ * @maxItems 1024
27
+ */
28
+ evidence: {
29
+ path: string;
30
+ line: number;
31
+ reason: string;
32
+ }[];
33
+ };
34
+ database: {
35
+ mode: 'none' | 'sql' | 'unsupported';
36
+ changeType: 'none' | 'code_only' | 'schema_only' | 'code_and_schema';
37
+ requiresCodeChanges: boolean;
38
+ requiresSchemaMigration: boolean;
39
+ desiredSchemaPath: string;
40
+ /**
41
+ * @maxItems 128
42
+ */
43
+ migrationPaths: string[];
44
+ /**
45
+ * @maxItems 512
46
+ */
47
+ changedFiles: string[];
48
+ /**
49
+ * @maxItems 128
50
+ */
51
+ evidence: {
52
+ path: string;
53
+ line: number;
54
+ reason: string;
55
+ }[];
56
+ /**
57
+ * @maxItems 64
58
+ */
59
+ unsupportedFeatures: string[];
60
+ };
61
+ application: {
62
+ runtime: string;
63
+ buildKind: 'container' | 'static';
64
+ topology: 'static' | 'backend' | 'frontend_and_backend';
65
+ entrypoint: string;
66
+ build: {
67
+ /**
68
+ * @minItems 1
69
+ * @maxItems 64
70
+ */
71
+ installCommand: string[];
72
+ /**
73
+ * @minItems 1
74
+ * @maxItems 64
75
+ */
76
+ buildCommand: string[];
77
+ /**
78
+ * @minItems 1
79
+ * @maxItems 16
80
+ */
81
+ outputs: {
82
+ path: string;
83
+ kind: 'public' | 'backend';
84
+ route: string;
85
+ }[];
86
+ };
87
+ basePath: {
88
+ required: boolean;
89
+ environmentVariable: 'HARBOUR_BASE_PATH';
90
+ };
91
+ /**
92
+ * @minItems 1
93
+ * @maxItems 64
94
+ */
95
+ evidence: {
96
+ path: string;
97
+ line: number;
98
+ reason: string;
99
+ }[];
100
+ /**
101
+ * @maxItems 64
102
+ */
103
+ unsupportedFeatures: string[];
104
+ };
105
+ }
@@ -0,0 +1,52 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ export const States = {
5
+ QUEUED: "QUEUED",
6
+ ANALYZING: "ANALYZING",
7
+ TRANSFORMING: "TRANSFORMING",
8
+ VALIDATING: "VALIDATING",
9
+ BUILDING: "BUILDING",
10
+ VERIFYING: "VERIFYING",
11
+ GATING: "GATING",
12
+ LIVE: "LIVE",
13
+ AWAITINGPROMOTION: "AWAITING-PROMOTION",
14
+ PROMOTING: "PROMOTING",
15
+ FAILED: "FAILED",
16
+ STALE: "STALE",
17
+ } as const;
18
+ export type StateName = (typeof States)[keyof typeof States];
19
+
20
+ export const Environments = {
21
+ Preview: "preview",
22
+ Prod: "prod",
23
+ } as const;
24
+ export type EnvironmentName = (typeof Environments)[keyof typeof Environments];
25
+
26
+ const legalTransitions: Record<string, Record<string, Record<string, true>>> = {
27
+ "preview": {
28
+ "QUEUED": { "ANALYZING": true, "FAILED": true, "STALE": true },
29
+ "ANALYZING": { "BUILDING": true, "TRANSFORMING": true, "FAILED": true, "STALE": true },
30
+ "TRANSFORMING": { "VALIDATING": true, "FAILED": true, "STALE": true },
31
+ "VALIDATING": { "BUILDING": true, "TRANSFORMING": true, "FAILED": true, "STALE": true },
32
+ "BUILDING": { "VERIFYING": true, "FAILED": true, "STALE": true },
33
+ "VERIFYING": { "GATING": true, "FAILED": true, "STALE": true },
34
+ "GATING": { "LIVE": true, "FAILED": true, "STALE": true },
35
+ "LIVE": { "AWAITING-PROMOTION": true },
36
+ "AWAITING-PROMOTION": { "PROMOTING": true },
37
+ "PROMOTING": { "LIVE": true, "FAILED": true },
38
+ },
39
+ "prod": {
40
+ "QUEUED": { "PROMOTING": true, "FAILED": true },
41
+ "PROMOTING": { "LIVE": true, "FAILED": true },
42
+ },
43
+ };
44
+ const terminalStates = new Set<string>(["FAILED", "STALE"]);
45
+
46
+ /** True iff from→to is a legal edge for environment in the T1a state machine. */
47
+ export function canTransition(environment: string, from: string, to: string): boolean {
48
+ return Boolean(legalTransitions[environment]?.[from]?.[to]);
49
+ }
50
+
51
+ /** True iff the state is terminal (FAILED, STALE). */
52
+ export function isTerminal(state: string): boolean { return terminalStates.has(state); }
@@ -0,0 +1,19 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ /**
5
+ * The app-version state machine T1a (LLD §1). Root type is the state token itself. `FAILED` carries a separate error category (see error-category.enum.json); the state token is the bare word `FAILED`. Legal edges live in x-transitions; codegen turns them into a shared transition-validation table so control-plane and any consumer agree on legality.
6
+ */
7
+ export type VersionState =
8
+ | 'QUEUED'
9
+ | 'ANALYZING'
10
+ | 'TRANSFORMING'
11
+ | 'VALIDATING'
12
+ | 'BUILDING'
13
+ | 'VERIFYING'
14
+ | 'GATING'
15
+ | 'LIVE'
16
+ | 'AWAITING-PROMOTION'
17
+ | 'PROMOTING'
18
+ | 'FAILED'
19
+ | 'STALE';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fourier-labs/harbour-contracts",
3
- "version": "0.5.0",
3
+ "version": "0.9.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,
@@ -11,7 +11,10 @@
11
11
  "type": "module",
12
12
  "files": [
13
13
  "dist",
14
- "schema"
14
+ "schema",
15
+ "fixtures/ai",
16
+ "gen-ts",
17
+ "contract-release.json"
15
18
  ],
16
19
  "main": "dist/gen-ts/index.js",
17
20
  "types": "dist/gen-ts/index.d.ts",
@@ -28,12 +31,13 @@
28
31
  "scripts": {
29
32
  "gen": "node tools/gen/gen.mjs",
30
33
  "build": "tsc -p tsconfig.json",
31
- "test": "node --test tools/gen/*.test.mjs resolver/*.test.mjs",
32
- "verify": "node tools/gen/gen.mjs && { [ -z \"$(git status --porcelain -- gen-ts gen-go)\" ] || { echo 'STALE: generated code out of date or uncommitted — run make gen and commit'; exit 1; }; }"
34
+ "test": "node --experimental-strip-types --test tools/gen/*.test.mjs resolver/*.test.mjs",
35
+ "verify": "node tools/release/verify-release.mjs && node tools/gen/gen.mjs && { [ -z \"$(git status --porcelain -- gen-ts gen-go contract-release.json)\" ] || { echo 'STALE: generated code or release manifest out of date or uncommitted — run make gen and commit'; exit 1; }; }"
33
36
  },
34
37
  "devDependencies": {
35
38
  "ajv": "^8.17.1",
36
39
  "ajv-formats": "^3.0.1",
40
+ "@types/node": "^22.10.2",
37
41
  "json-schema-to-typescript": "^15.0.4",
38
42
  "typescript": "^5.7.2"
39
43
  }
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/ai-budget-ledger-event.schema.json",
4
+ "title": "AiBudgetLedgerEvent",
5
+ "description": "Append-only v2 committed-exposure ledger event. Amounts are integer micro-USD and replay is identified by idempotencyKey.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema", "eventId", "idempotencyKey", "budgetScope", "period", "eventType", "reservationId", "amountMicroUsd", "committedMicroUsdAfter", "eventDigest", "occurredAt"],
9
+ "properties": {
10
+ "schema": { "type": "string", "const": "harbour.ai-budget-ledger-event/2.0" },
11
+ "eventId": { "$ref": "#/definitions/AiBudgetIdentifier" },
12
+ "idempotencyKey": { "$ref": "#/definitions/AiBudgetIdentifier" },
13
+ "budgetScope": { "$ref": "#/definitions/AiBudgetIdentifier" },
14
+ "period": { "$ref": "#/definitions/AiBudgetIdentifier" },
15
+ "eventType": { "type": "string", "enum": ["RESERVED", "SETTLED", "REPAIRED", "ADJUSTED"] },
16
+ "reservationId": { "$ref": "#/definitions/AiBudgetIdentifier" },
17
+ "requestId": { "$ref": "#/definitions/AiBudgetIdentifier" },
18
+ "amountMicroUsd": { "type": "integer" },
19
+ "committedMicroUsdAfter": { "type": "integer", "minimum": 0 },
20
+ "pricingRevisionDigest": { "$ref": "#/definitions/AiBudgetDigest" },
21
+ "supersedesEventId": { "$ref": "#/definitions/AiBudgetIdentifier" },
22
+ "eventDigest": { "$ref": "#/definitions/AiBudgetDigest" },
23
+ "occurredAt": { "type": "string", "format": "date-time" }
24
+ },
25
+ "definitions": {
26
+ "AiBudgetIdentifier": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "^[A-Za-z0-9][A-Za-z0-9_.@/#-]*$" },
27
+ "AiBudgetDigest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }
28
+ }
29
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/ai-classification-stamp.schema.json",
4
+ "title": "AiClassificationStamp",
5
+ "description": "Immutable, value-free classification evidence for one exact version and source revision. Unavailable evidence is explicit and never interpreted as clean.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema", "stampId", "tenantId", "appId", "environment", "versionId", "sourceRevision", "status", "classifierId", "classifierVersion", "sourceDigest", "evidenceDigest", "stampDigest", "stampedAt"],
9
+ "properties": {
10
+ "schema": { "type": "string", "const": "harbour.ai-classification-stamp/1.0" },
11
+ "stampId": { "$ref": "#/definitions/AiClassificationIdentifier" },
12
+ "tenantId": { "$ref": "#/definitions/AiClassificationIdentifier" },
13
+ "appId": { "$ref": "#/definitions/AiClassificationIdentifier" },
14
+ "environment": { "$ref": "#/definitions/AiClassificationIdentifier" },
15
+ "versionId": { "type": "string", "pattern": "^[^#]+#[^#]+#[^#]+#[0-9]{6,}$", "maxLength": 320 },
16
+ "sourceRevision": { "type": "string", "minLength": 7, "maxLength": 128 },
17
+ "status": { "type": "string", "enum": ["AVAILABLE", "UNAVAILABLE"] },
18
+ "classification": { "type": "string", "enum": ["Green", "Yellow", "Orange", "Red"] },
19
+ "classifierId": { "$ref": "#/definitions/AiClassificationIdentifier" },
20
+ "classifierVersion": { "type": "string", "minLength": 1, "maxLength": 128 },
21
+ "sourceDigest": { "$ref": "#/definitions/AiClassificationDigest" },
22
+ "inventoryDigest": { "$ref": "#/definitions/AiClassificationDigest" },
23
+ "securityEvidenceDigest": { "$ref": "#/definitions/AiClassificationDigest" },
24
+ "evidenceDigest": { "$ref": "#/definitions/AiClassificationDigest" },
25
+ "stampDigest": { "$ref": "#/definitions/AiClassificationDigest" },
26
+ "unavailableReason": { "$ref": "#/definitions/AiClassificationIdentifier" },
27
+ "stampedAt": { "type": "string", "format": "date-time" }
28
+ },
29
+ "allOf": [
30
+ {
31
+ "if": { "properties": { "status": { "const": "AVAILABLE" } }, "required": ["status"] },
32
+ "then": { "required": ["classification", "inventoryDigest", "securityEvidenceDigest"], "not": { "required": ["unavailableReason"] } },
33
+ "else": { "required": ["unavailableReason"], "not": { "anyOf": [{ "required": ["classification"] }, { "required": ["inventoryDigest"] }, { "required": ["securityEvidenceDigest"] }] } }
34
+ }
35
+ ],
36
+ "definitions": {
37
+ "AiClassificationIdentifier": { "type": "string", "minLength": 1, "maxLength": 160, "pattern": "^[A-Za-z0-9][A-Za-z0-9_.@/-]*$" },
38
+ "AiClassificationDigest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }
39
+ }
40
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/ai-composite-policy-digest.schema.json",
4
+ "title": "AiCompositePolicyDigestInput",
5
+ "description": "The complete ordered identity input for one composite policy digest. The digest is SHA-256 over the UTF-8 preimage specified by x-digest; missing optional layer digests use the literal hyphen sentinel.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema", "baselineDigest", "classificationStampDigest"],
9
+ "properties": {
10
+ "schema": { "type": "string", "const": "harbour.ai-composite-policy-digest/1.0" },
11
+ "baselineDigest": { "$ref": "#/definitions/AiCompositePolicyDigest" },
12
+ "classificationStampDigest": { "$ref": "#/definitions/AiCompositePolicyDigest" },
13
+ "tenantPolicyDigest": { "$ref": "#/definitions/AiCompositePolicyDigest" },
14
+ "applicationPolicyDigest": { "$ref": "#/definitions/AiCompositePolicyDigest" },
15
+ "versionOverrideDigest": { "$ref": "#/definitions/AiCompositePolicyDigest" }
16
+ },
17
+ "definitions": {
18
+ "AiCompositePolicyDigest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }
19
+ },
20
+ "x-digest": {
21
+ "algorithm": "sha256",
22
+ "encoding": "utf8",
23
+ "domain": "harbour.ai-composite-policy-digest/1",
24
+ "absent": "-",
25
+ "order": ["baselineDigest", "classificationStampDigest", "tenantPolicyDigest", "applicationPolicyDigest", "versionOverrideDigest"]
26
+ }
27
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/ai-deployment-gate-decision.schema.json",
4
+ "title": "AiDeploymentGateDecision",
5
+ "description": "One append-only deployment gate decision. Corrections are new records linked by supersedesDecisionId; existing records are never overwritten.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema", "decisionId", "sequence", "tenantId", "appId", "environment", "versionId", "sourceRevision", "decision", "reasonCodes", "classificationStampDigest", "planDigest", "policyDigest", "evidenceDigest", "decisionDigest", "decidedAt"],
9
+ "properties": {
10
+ "schema": { "type": "string", "const": "harbour.ai-deployment-gate-decision/1.0" },
11
+ "decisionId": { "$ref": "#/definitions/AiGateIdentifier" },
12
+ "sequence": { "type": "integer", "minimum": 1 },
13
+ "supersedesDecisionId": { "$ref": "#/definitions/AiGateIdentifier" },
14
+ "tenantId": { "$ref": "#/definitions/AiGateIdentifier" },
15
+ "appId": { "$ref": "#/definitions/AiGateIdentifier" },
16
+ "environment": { "$ref": "#/definitions/AiGateIdentifier" },
17
+ "versionId": { "type": "string", "pattern": "^[^#]+#[^#]+#[^#]+#[0-9]{6,}$", "maxLength": 320 },
18
+ "sourceRevision": { "type": "string", "minLength": 7, "maxLength": 128 },
19
+ "decision": { "type": "string", "enum": ["PASS", "BLOCKED"] },
20
+ "reasonCodes": { "type": "array", "maxItems": 64, "uniqueItems": true, "items": { "$ref": "#/definitions/AiGateIdentifier" } },
21
+ "classificationStampDigest": { "$ref": "#/definitions/AiGateDigest" },
22
+ "planDigest": { "$ref": "#/definitions/AiGateDigest" },
23
+ "policyDigest": { "$ref": "#/definitions/AiGateDigest" },
24
+ "evidenceDigest": { "$ref": "#/definitions/AiGateDigest" },
25
+ "decisionDigest": { "$ref": "#/definitions/AiGateDigest" },
26
+ "decidedAt": { "type": "string", "format": "date-time" }
27
+ },
28
+ "allOf": [
29
+ {
30
+ "if": { "properties": { "decision": { "const": "BLOCKED" } }, "required": ["decision"] },
31
+ "then": { "properties": { "reasonCodes": { "minItems": 1 } } }
32
+ }
33
+ ],
34
+ "definitions": {
35
+ "AiGateIdentifier": { "type": "string", "minLength": 1, "maxLength": 160, "pattern": "^[A-Za-z0-9][A-Za-z0-9_.@/-]*$" },
36
+ "AiGateDigest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }
37
+ }
38
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/ai-gateway-key-lifecycle-event.schema.json",
4
+ "title": "AiGatewayKeyLifecycleEvent",
5
+ "description": "Append-only, value-free lifecycle evidence for one version-scoped Gateway key.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema", "eventId", "sequence", "keyId", "tenantId", "appId", "environment", "versionId", "fromStatus", "toStatus", "cause", "eventDigest", "occurredAt"],
9
+ "properties": {
10
+ "schema": { "type": "string", "const": "harbour.ai-gateway-key-lifecycle-event/1.0" },
11
+ "eventId": { "$ref": "#/definitions/AiKeyLifecycleIdentifier" },
12
+ "sequence": { "type": "integer", "minimum": 1 },
13
+ "keyId": { "type": "string", "pattern": "^[0-9a-f]{32}$" },
14
+ "tenantId": { "$ref": "#/definitions/AiKeyLifecycleIdentifier" },
15
+ "appId": { "$ref": "#/definitions/AiKeyLifecycleIdentifier" },
16
+ "environment": { "$ref": "#/definitions/AiKeyLifecycleIdentifier" },
17
+ "versionId": { "type": "string", "pattern": "^[^#]+#[^#]+#[^#]+#[0-9]{6,}$", "maxLength": 320 },
18
+ "fromStatus": { "$ref": "#/definitions/AiKeyLifecycleStatus" },
19
+ "toStatus": { "$ref": "#/definitions/AiKeyLifecycleStatus" },
20
+ "cause": { "type": "string", "enum": ["PLAN", "ACTIVATION", "FAILED", "STALE", "SUPERSEDED", "VERSION_DELETED", "MANUAL_REVOKE", "OVERLAP_EXPIRED", "RECONCILIATION"] },
21
+ "secretRef": { "$ref": "#/definitions/AiKeyLifecycleSafeReference" },
22
+ "overlapExpiresAt": { "type": "string", "format": "date-time" },
23
+ "eventDigest": { "$ref": "#/definitions/AiKeyLifecycleDigest" },
24
+ "occurredAt": { "type": "string", "format": "date-time" }
25
+ },
26
+ "definitions": {
27
+ "AiKeyLifecycleIdentifier": { "type": "string", "minLength": 1, "maxLength": 160, "pattern": "^[A-Za-z0-9][A-Za-z0-9_.@/-]*$" },
28
+ "AiKeyLifecycleDigest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
29
+ "AiKeyLifecycleStatus": { "type": "string", "enum": ["NONE", "PENDING", "ACTIVE", "REVOKED", "DELETION_SCHEDULED", "DELETED"] },
30
+ "AiKeyLifecycleSafeReference": {
31
+ "description": "Value-free hierarchical secret reference using the additive reviewed scheme registry; no userinfo, query, fragment, port or percent encoding.",
32
+ "type": "string",
33
+ "maxLength": 1024,
34
+ "pattern": "^(?:aws-sm|iam-role|k8s-sa|secretref|spiffe|vault|vault-v2)://[A-Za-z0-9][A-Za-z0-9._-]{0,127}(?:/[A-Za-z0-9][A-Za-z0-9._~+=-]{0,255})*$"
35
+ }
36
+ },
37
+ "allOf": [
38
+ {
39
+ "oneOf": [
40
+ { "properties": { "fromStatus": { "const": "NONE" }, "toStatus": { "const": "PENDING" } } },
41
+ { "properties": { "fromStatus": { "const": "PENDING" }, "toStatus": { "const": "ACTIVE" } } },
42
+ { "properties": { "fromStatus": { "const": "PENDING" }, "toStatus": { "const": "REVOKED" } } },
43
+ { "properties": { "fromStatus": { "const": "ACTIVE" }, "toStatus": { "const": "REVOKED" } } },
44
+ { "properties": { "fromStatus": { "const": "REVOKED" }, "toStatus": { "const": "DELETION_SCHEDULED" } } },
45
+ { "properties": { "fromStatus": { "const": "DELETION_SCHEDULED" }, "toStatus": { "const": "DELETED" } } }
46
+ ]
47
+ }
48
+ ],
49
+ "x-safe-reference-pattern": "^(?:aws-sm|iam-role|k8s-sa|secretref|spiffe|vault|vault-v2)://[A-Za-z0-9][A-Za-z0-9._-]{0,127}(?:/[A-Za-z0-9][A-Za-z0-9._~+=-]{0,255})*$",
50
+ "x-approved-reference-schemes": ["aws-sm", "iam-role", "k8s-sa", "secretref", "spiffe", "vault", "vault-v2"],
51
+ "x-status-definition": "AiKeyLifecycleStatus",
52
+ "x-terminal": ["DELETED"],
53
+ "x-transitions": [
54
+ { "from": "NONE", "to": "PENDING" },
55
+ { "from": "PENDING", "to": "ACTIVE" },
56
+ { "from": "PENDING", "to": "REVOKED" },
57
+ { "from": "ACTIVE", "to": "REVOKED" },
58
+ { "from": "REVOKED", "to": "DELETION_SCHEDULED" },
59
+ { "from": "DELETION_SCHEDULED", "to": "DELETED" }
60
+ ]
61
+ }
@@ -58,7 +58,7 @@
58
58
  "sourceRevision": { "type": "string", "minLength": 7, "maxLength": 128 },
59
59
  "mode": { "type": "string", "enum": ["observe", "enforce"] },
60
60
  "gatewayBaseUrl": { "type": "string", "format": "uri", "maxLength": 512, "pattern": "^https://[^?#]+/v1$" },
61
- "gatewayKeySecretRef": { "type": "string", "maxLength": 512, "pattern": "^aws-sm://[A-Za-z0-9/_+=.@-]+$" },
61
+ "gatewayKeySecretRef": { "type": "string", "maxLength": 512, "pattern": "^aws-sm://[A-Za-z0-9][A-Za-z0-9._-]{0,127}(?:/[A-Za-z0-9][A-Za-z0-9._~+=-]{0,255})*$" },
62
62
  "modelAliases": {
63
63
  "type": "object",
64
64
  "additionalProperties": false,
@@ -69,9 +69,28 @@
69
69
  }
70
70
  },
71
71
  "policyDigest": { "$ref": "#/definitions/AiDecisionDigest" },
72
+ "runtimeBinding": { "$ref": "#/definitions/AiDecisionRuntimeBinding" },
72
73
  "requiredStrategies": { "type": "array", "minItems": 1, "maxItems": 2, "uniqueItems": true, "items": { "type": "string", "enum": ["deployment_injection", "deterministic_adapter"] } }
73
74
  }
74
75
  },
76
+ "AiDecisionRuntimeBinding": {
77
+ "type": "object",
78
+ "additionalProperties": false,
79
+ "required": ["schema", "platform", "workloadIdentityRef", "secretRef", "runtimeBindingDigest"],
80
+ "properties": {
81
+ "schema": { "type": "string", "const": "harbour.ai-runtime-binding/1.0" },
82
+ "platform": { "type": "string", "minLength": 1, "maxLength": 64, "pattern": "^[a-z][a-z0-9_.-]*$" },
83
+ "workloadIdentityRef": { "$ref": "#/definitions/AiDecisionSafeReference" },
84
+ "secretRef": { "$ref": "#/definitions/AiDecisionSafeReference" },
85
+ "runtimeBindingDigest": { "$ref": "#/definitions/AiDecisionDigest" }
86
+ }
87
+ },
88
+ "AiDecisionSafeReference": {
89
+ "description": "Platform-neutral value-free hierarchical reference using the additive reviewed scheme registry; no userinfo, query, fragment, port or percent encoding.",
90
+ "type": "string",
91
+ "maxLength": 1024,
92
+ "pattern": "^(?:aws-sm|iam-role|k8s-sa|secretref|spiffe|vault|vault-v2)://[A-Za-z0-9][A-Za-z0-9._-]{0,127}(?:/[A-Za-z0-9][A-Za-z0-9._~+=-]{0,255})*$"
93
+ },
75
94
  "AiInterceptionPlanEntry": {
76
95
  "type": "object",
77
96
  "additionalProperties": false,
@@ -101,5 +120,7 @@
101
120
  }
102
121
  ]
103
122
  }
104
- }
123
+ },
124
+ "x-safe-reference-pattern": "^(?:aws-sm|iam-role|k8s-sa|secretref|spiffe|vault|vault-v2)://[A-Za-z0-9][A-Za-z0-9._-]{0,127}(?:/[A-Za-z0-9][A-Za-z0-9._~+=-]{0,255})*$",
125
+ "x-approved-reference-schemes": ["aws-sm", "iam-role", "k8s-sa", "secretref", "spiffe", "vault", "vault-v2"]
105
126
  }
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/ai-metering-policy.schema.json",
4
+ "title": "AiMeteringPolicy",
5
+ "description": "Additive v2 metering-policy binding. It binds a composite policy to immutable pricing revisions without changing the v1 policy snapshot.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema", "policyDigest", "pricingRevisionDigests", "reservationMode", "policyDigestV2", "publishedAt"],
9
+ "properties": {
10
+ "schema": { "type": "string", "const": "harbour.ai-metering-policy/2.0" },
11
+ "policyDigest": { "$ref": "#/definitions/AiMeteringPolicyDigest" },
12
+ "pricingRevisionDigests": { "type": "array", "minItems": 1, "maxItems": 512, "uniqueItems": true, "items": { "$ref": "#/definitions/AiMeteringPolicyDigest" } },
13
+ "reservationMode": { "type": "string", "const": "committed_exposure" },
14
+ "policyDigestV2": { "$ref": "#/definitions/AiMeteringPolicyDigest" },
15
+ "publishedAt": { "type": "string", "format": "date-time" }
16
+ },
17
+ "definitions": { "AiMeteringPolicyDigest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" } }
18
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/ai-pricing-revision.schema.json",
4
+ "title": "AiPricingRevision",
5
+ "description": "Immutable v2 line-item pricing revision. A correction is a new revision; published records are never edited.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema", "revisionId", "provider", "providerModel", "currency", "dimensions", "revisionDigest", "effectiveAt", "publishedAt"],
9
+ "properties": {
10
+ "schema": { "type": "string", "const": "harbour.ai-pricing-revision/2.0" },
11
+ "revisionId": { "$ref": "#/definitions/AiPricingIdentifier" },
12
+ "provider": { "type": "string", "enum": ["openai", "anthropic", "gemini", "openrouter"] },
13
+ "providerModel": { "type": "string", "minLength": 1, "maxLength": 256 },
14
+ "region": { "$ref": "#/definitions/AiPricingIdentifier" },
15
+ "currency": { "type": "string", "const": "USD" },
16
+ "dimensions": { "type": "array", "minItems": 1, "maxItems": 64, "items": { "$ref": "#/definitions/PriceDimension" } },
17
+ "supersedesRevisionId": { "$ref": "#/definitions/AiPricingIdentifier" },
18
+ "revisionDigest": { "$ref": "#/definitions/AiPricingDigest" },
19
+ "effectiveAt": { "type": "string", "format": "date-time" },
20
+ "publishedAt": { "type": "string", "format": "date-time" }
21
+ },
22
+ "definitions": {
23
+ "AiPricingIdentifier": { "type": "string", "minLength": 1, "maxLength": 160, "pattern": "^[A-Za-z0-9][A-Za-z0-9_.@/-]*$" },
24
+ "AiPricingDigest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
25
+ "PriceDimension": {
26
+ "type": "object",
27
+ "additionalProperties": false,
28
+ "required": ["dimension", "direction", "unit", "scale", "microUsdPerScale"],
29
+ "properties": {
30
+ "dimension": { "$ref": "#/definitions/AiPricingIdentifier" },
31
+ "direction": { "type": "string", "enum": ["input", "output"] },
32
+ "unit": { "$ref": "#/definitions/AiPricingIdentifier" },
33
+ "scale": { "type": "integer", "minimum": 1 },
34
+ "microUsdPerScale": { "type": "integer", "minimum": 0 }
35
+ }
36
+ }
37
+ }
38
+ }