@fourier-labs/harbour-contracts 0.6.0 → 0.10.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 (134) hide show
  1. package/README.md +49 -4
  2. package/contract-release.json +146 -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 +12 -0
  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-pricing-revision.d.ts +29 -0
  19. package/dist/gen-ts/ai-pricing-revision.js +3 -0
  20. package/dist/gen-ts/ai-runtime-binding.d.ts +14 -0
  21. package/dist/gen-ts/ai-runtime-binding.js +3 -0
  22. package/dist/gen-ts/ai-runtime-contract.d.ts +15 -0
  23. package/dist/gen-ts/ai-trace-event-v2.d.ts +31 -0
  24. package/dist/gen-ts/ai-trace-event-v2.js +3 -0
  25. package/dist/gen-ts/ai-verification-evidence.d.ts +1 -0
  26. package/dist/gen-ts/binding-classification-digest-helper.d.ts +11 -0
  27. package/dist/gen-ts/binding-classification-digest-helper.js +47 -0
  28. package/dist/gen-ts/binding-classification-subject.d.ts +56 -0
  29. package/dist/gen-ts/binding-classification-subject.js +3 -0
  30. package/dist/gen-ts/contract-release.d.ts +7 -0
  31. package/dist/gen-ts/contract-release.js +9 -0
  32. package/dist/gen-ts/deploy-request.d.ts +52 -0
  33. package/dist/gen-ts/deploy-response.d.ts +8 -0
  34. package/dist/gen-ts/index.d.ts +15 -0
  35. package/dist/gen-ts/index.js +15 -0
  36. package/dist/gen-ts/promotion-request.d.ts +52 -0
  37. package/dist/gen-ts/promotion-response.d.ts +8 -0
  38. package/dist/gen-ts/sanitized-binding-digest-helper.d.ts +7 -0
  39. package/dist/gen-ts/sanitized-binding-digest-helper.js +58 -0
  40. package/dist/gen-ts/sanitized-binding-evidence.d.ts +47 -0
  41. package/dist/gen-ts/sanitized-binding-evidence.js +3 -0
  42. package/fixtures/ai/adversarial-binding-classification-value.json +28 -0
  43. package/fixtures/ai/adversarial-sanitized-binding-relabel.json +32 -0
  44. package/fixtures/ai/valid-binding-classification-subject.json +46 -0
  45. package/fixtures/ai/valid-blocked-trace-event.json +31 -0
  46. package/fixtures/ai/valid-budget-ledger-event.json +15 -0
  47. package/fixtures/ai/valid-classification-stamp.json +19 -0
  48. package/fixtures/ai/valid-composite-policy-digest.json +11 -0
  49. package/fixtures/ai/valid-deployment-gate-decision.json +18 -0
  50. package/fixtures/ai/valid-failed-trace-event.json +50 -0
  51. package/fixtures/ai/valid-gateway-key-lifecycle-event.json +16 -0
  52. package/fixtures/ai/valid-gateway-key-lookup.json +14 -0
  53. package/fixtures/ai/valid-governance-decision.json +53 -0
  54. package/fixtures/ai/valid-inventory.json +30 -0
  55. package/fixtures/ai/valid-metering-policy.json +8 -0
  56. package/fixtures/ai/valid-policy-snapshot.json +117 -0
  57. package/fixtures/ai/valid-pricing-revision.json +14 -0
  58. package/fixtures/ai/valid-runtime-binding.json +7 -0
  59. package/fixtures/ai/valid-runtime-contract.json +24 -0
  60. package/fixtures/ai/valid-sanitized-binding-evidence.json +32 -0
  61. package/fixtures/ai/valid-source-revision-digest.json +6 -0
  62. package/fixtures/ai/valid-trace-event-v2.json +24 -0
  63. package/fixtures/ai/valid-trace-event.json +41 -0
  64. package/fixtures/ai/valid-transform-event.json +32 -0
  65. package/fixtures/ai/valid-transform-result.json +23 -0
  66. package/fixtures/ai/valid-verification-evidence.json +33 -0
  67. package/gen-ts/ai-budget-ledger-event.ts +25 -0
  68. package/gen-ts/ai-callsite-inventory.ts +64 -0
  69. package/gen-ts/ai-classification-stamp.ts +28 -0
  70. package/gen-ts/ai-composite-policy-digest-helper.ts +18 -0
  71. package/gen-ts/ai-composite-policy-digest.ts +16 -0
  72. package/gen-ts/ai-deployment-gate-decision.ts +30 -0
  73. package/gen-ts/ai-gateway-key-lifecycle-event.ts +81 -0
  74. package/gen-ts/ai-gateway-key-lookup.ts +23 -0
  75. package/gen-ts/ai-gateway-key-token.ts +7 -0
  76. package/gen-ts/ai-governance-decision.ts +90 -0
  77. package/gen-ts/ai-metering-policy.ts +20 -0
  78. package/gen-ts/ai-policy-snapshot.ts +169 -0
  79. package/gen-ts/ai-pricing-revision.ts +33 -0
  80. package/gen-ts/ai-runtime-binding.ts +18 -0
  81. package/gen-ts/ai-runtime-contract.ts +46 -0
  82. package/gen-ts/ai-trace-event-v2.ts +35 -0
  83. package/gen-ts/ai-trace-event.ts +72 -0
  84. package/gen-ts/ai-transform-event.ts +51 -0
  85. package/gen-ts/ai-transform-result.ts +36 -0
  86. package/gen-ts/ai-verification-evidence.ts +54 -0
  87. package/gen-ts/api-error-response.ts +16 -0
  88. package/gen-ts/artifact-contract.ts +81 -0
  89. package/gen-ts/binding-classification-digest-helper.ts +44 -0
  90. package/gen-ts/binding-classification-subject.ts +60 -0
  91. package/gen-ts/contract-release.ts +10 -0
  92. package/gen-ts/data-dependency.ts +46 -0
  93. package/gen-ts/data-grant.ts +71 -0
  94. package/gen-ts/data-source.ts +78 -0
  95. package/gen-ts/deploy-request.ts +117 -0
  96. package/gen-ts/deploy-response.ts +33 -0
  97. package/gen-ts/deployment-environment.ts +7 -0
  98. package/gen-ts/deployment-source.ts +16 -0
  99. package/gen-ts/deployment-status-failure.ts +12 -0
  100. package/gen-ts/deployment-status-request.ts +11 -0
  101. package/gen-ts/deployment-status-response.ts +39 -0
  102. package/gen-ts/env-bindings.ts +64 -0
  103. package/gen-ts/error-category-registry.ts +108 -0
  104. package/gen-ts/error-category.ts +7 -0
  105. package/gen-ts/error-envelope.ts +49 -0
  106. package/gen-ts/index.ts +51 -0
  107. package/gen-ts/kvs-routing-entry.ts +30 -0
  108. package/gen-ts/promotion-request.ts +95 -0
  109. package/gen-ts/promotion-response.ts +28 -0
  110. package/gen-ts/sanitized-binding-digest-helper.ts +33 -0
  111. package/gen-ts/sanitized-binding-evidence.ts +51 -0
  112. package/gen-ts/secret-reference.ts +40 -0
  113. package/gen-ts/transform-report.ts +105 -0
  114. package/gen-ts/version-state-machine.ts +52 -0
  115. package/gen-ts/version-state.ts +19 -0
  116. package/package.json +8 -4
  117. package/schema/ai-budget-ledger-event.schema.json +29 -0
  118. package/schema/ai-classification-stamp.schema.json +40 -0
  119. package/schema/ai-composite-policy-digest.schema.json +27 -0
  120. package/schema/ai-deployment-gate-decision.schema.json +38 -0
  121. package/schema/ai-gateway-key-lifecycle-event.schema.json +61 -0
  122. package/schema/ai-governance-decision.schema.json +23 -2
  123. package/schema/ai-metering-policy.schema.json +18 -0
  124. package/schema/ai-pricing-revision.schema.json +38 -0
  125. package/schema/ai-runtime-binding.schema.json +26 -0
  126. package/schema/ai-runtime-contract.schema.json +24 -2
  127. package/schema/ai-trace-event-v2.schema.json +41 -0
  128. package/schema/ai-verification-evidence.schema.json +1 -0
  129. package/schema/binding-classification-subject.schema.json +119 -0
  130. package/schema/deploy-request.schema.json +37 -2
  131. package/schema/deploy-response.schema.json +11 -1
  132. package/schema/promotion-request.schema.json +5 -1
  133. package/schema/promotion-response.schema.json +11 -1
  134. package/schema/sanitized-binding-evidence.schema.json +102 -0
@@ -0,0 +1,35 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ export type AiTraceV2Digest = string;
5
+ export type AiTraceV2Identifier = string;
6
+
7
+ /**
8
+ * Additive metering projection linked to a readable v1 trace. It carries dimension-level reported or estimated usage without changing the v1 event.
9
+ */
10
+ export interface AiTraceEventV2 {
11
+ schema: 'harbour.ai-trace-event/2.0';
12
+ traceId: string;
13
+ requestId: string;
14
+ policyDigest: AiTraceV2Digest;
15
+ outcome: 'succeeded' | 'blocked' | 'failed' | 'cancelled';
16
+ /**
17
+ * @maxItems 256
18
+ */
19
+ usageLineItems: UsageLineItem[];
20
+ totalCostMicroUsd: number;
21
+ reservationId?: string;
22
+ meteringStatus: 'settled' | 'reserved' | 'unknown' | 'repaired';
23
+ occurredAt: string;
24
+ }
25
+ export interface UsageLineItem {
26
+ attempt: number;
27
+ dimension: AiTraceV2Identifier;
28
+ direction: 'input' | 'output';
29
+ quantity: number;
30
+ unit: AiTraceV2Identifier;
31
+ priceRevisionDigest: AiTraceV2Digest;
32
+ scale: number;
33
+ costMicroUsd: number;
34
+ provenance: 'reported' | 'estimated';
35
+ }
@@ -0,0 +1,72 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ /**
5
+ * Value-free asynchronous AI Gateway trace metadata. Raw prompts and responses never enter the queue; captured content is referenced only after successful encrypted storage.
6
+ */
7
+ export type AiTraceEvent = {
8
+ schema: 'harbour.ai-trace-event/1.0';
9
+ traceId: string;
10
+ requestId: string;
11
+ tenantId: AiTraceIdentifier;
12
+ appId: AiTraceIdentifier;
13
+ environment: AiTraceIdentifier;
14
+ versionId: string;
15
+ callsiteFingerprint?: AiTraceDigest;
16
+ policyDigest: AiTraceDigest;
17
+ routeRevision?: string;
18
+ requestedModel?: string;
19
+ governedModelAlias?: string;
20
+ provider?: 'openai' | 'anthropic' | 'gemini' | 'openrouter';
21
+ providerModel?: string;
22
+ outcome: 'succeeded' | 'blocked' | 'failed' | 'cancelled';
23
+ usage?: AiTraceUsage;
24
+ priceRevision?: AiTraceIdentifier;
25
+ costMicroUsd: number;
26
+ latencyMs: number;
27
+ retryCount: number;
28
+ fallbackUsed: boolean;
29
+ reservationId?: string;
30
+ /**
31
+ * @minItems 0
32
+ * @maxItems 32
33
+ */
34
+ attempts: AiTraceAttempt[];
35
+ /**
36
+ * @maxItems 128
37
+ */
38
+ guardrailResults: AiTraceGuardrailResult[];
39
+ contentCaptureStatus: 'off' | 'captured' | 'capture_failed';
40
+ contentRef?: string;
41
+ retentionExpiresAt?: string;
42
+ occurredAt: string;
43
+ };
44
+ export type AiTraceIdentifier = string;
45
+ export type AiTraceDigest = string;
46
+ export type AiTraceAttempt = {
47
+ attempt: number;
48
+ provider: 'openai' | 'anthropic' | 'gemini' | 'openrouter';
49
+ providerModel: string;
50
+ priceRevision: AiTraceIdentifier;
51
+ outcome: 'succeeded' | 'failed' | 'timed_out' | 'cancelled';
52
+ /**
53
+ * Provider-reported token usage, or null when the attempt did not succeed and the provider reported none. Null states that usage is UNKNOWN — estimates are never fabricated. A succeeded attempt always reports real usage.
54
+ */
55
+ usage: AiTraceUsage | null;
56
+ costMicroUsd: number;
57
+ latencyMs: number;
58
+ errorCode?: string;
59
+ };
60
+
61
+ export interface AiTraceUsage {
62
+ inputTokens: number;
63
+ outputTokens: number;
64
+ }
65
+ export interface AiTraceGuardrailResult {
66
+ ruleId: string;
67
+ detectorId: string;
68
+ detectorVersion: string;
69
+ phase: 'input' | 'output';
70
+ action: 'ALLOW' | 'OBSERVE' | 'REDACT' | 'BLOCK';
71
+ result: 'passed' | 'matched' | 'blocked' | 'redacted';
72
+ }
@@ -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 AiTransformIdentifier = string;
5
+ export type AiTransformDigest = string;
6
+ export type AiTransformPlanEntry = {
7
+ callSiteFingerprint: AiTransformDigest;
8
+ sourcePath: string;
9
+ expectedSourceFileSha256: AiTransformDigest;
10
+ strategy: 'deployment_injection' | 'deterministic_adapter';
11
+ configurationMappingId?: AiTransformIdentifier;
12
+ configurationMappingVersion?: string;
13
+ transformId?: AiTransformIdentifier;
14
+ transformVersion?: string;
15
+ /**
16
+ * @minItems 1
17
+ * @maxItems 32
18
+ */
19
+ allowedSyntaxKinds: AiTransformIdentifier[];
20
+ runtimeShimName?: string;
21
+ runtimeShimVersion?: string;
22
+ runtimeShimDigest?: AiTransformDigest;
23
+ };
24
+
25
+ /**
26
+ * Checksum-bound deterministic AI interception task input. Entries are the complete ordered PLAN interception plan so the task can verify the governance plan digest before applying its deterministic subset; source and patches are referenced, never embedded.
27
+ */
28
+ export interface AiTransformEvent {
29
+ schema: 'harbour.ai-transform-event/1.0';
30
+ tenantId: AiTransformIdentifier;
31
+ appId: AiTransformIdentifier;
32
+ environment: AiTransformIdentifier;
33
+ versionId: string;
34
+ sourceRevision: string;
35
+ workspace: AiTransformWorkspace;
36
+ inventoryDigest: AiTransformDigest;
37
+ policyDigest: AiTransformDigest;
38
+ runtimeContractDigest: AiTransformDigest;
39
+ interceptionPlanDigest: AiTransformDigest;
40
+ /**
41
+ * @minItems 1
42
+ * @maxItems 1000
43
+ */
44
+ entries: AiTransformPlanEntry[];
45
+ }
46
+ export interface AiTransformWorkspace {
47
+ bucket: string;
48
+ key: string;
49
+ sha256: AiTransformDigest;
50
+ size: number;
51
+ }
@@ -0,0 +1,36 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ export type AiTransformResultDigest = string;
5
+ export type AiTransformResultIdentifier = string;
6
+
7
+ /**
8
+ * Bounded deterministic interception result; contains descriptors and digest evidence only.
9
+ */
10
+ export interface AiTransformResult {
11
+ schema: 'harbour.ai-transform-result/1.0';
12
+ workspace: AiTransformResultWorkspace;
13
+ interceptionPlanDigest: AiTransformResultDigest;
14
+ transformerVersion: string;
15
+ /**
16
+ * @minItems 1
17
+ * @maxItems 1000
18
+ */
19
+ applied: AiTransformApplied[];
20
+ }
21
+ export interface AiTransformResultWorkspace {
22
+ bucket: string;
23
+ key: string;
24
+ sha256: AiTransformResultDigest;
25
+ size: number;
26
+ }
27
+ export interface AiTransformApplied {
28
+ callSiteFingerprint: AiTransformResultDigest;
29
+ transformId: AiTransformResultIdentifier;
30
+ transformVersion: string;
31
+ sourcePath: string;
32
+ beforeSha256: AiTransformResultDigest;
33
+ afterSha256: AiTransformResultDigest;
34
+ governedBoundaryFingerprint: AiTransformResultDigest;
35
+ runtimeShimDigest: AiTransformResultDigest;
36
+ }
@@ -0,0 +1,54 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ export type AiVerificationIdentifier = string;
5
+ export type AiVerificationDigest = string;
6
+
7
+ /**
8
+ * Bounded, value-free evidence submitted by deployment verification for one governed version.
9
+ */
10
+ export interface AiVerificationEvidence {
11
+ schema: 'harbour.ai-verification-evidence/1.0';
12
+ tenantId: AiVerificationIdentifier;
13
+ appId: AiVerificationIdentifier;
14
+ environment: AiVerificationIdentifier;
15
+ versionId: string;
16
+ sourceRevision: string;
17
+ inventoryDigest: AiVerificationDigest;
18
+ interceptionPlanDigest: AiVerificationDigest;
19
+ runtimeContractDigest: AiVerificationDigest;
20
+ policyDigest: AiVerificationDigest;
21
+ runtimeBindingDigest?: AiVerificationDigest;
22
+ finalWorkspaceDigest: AiVerificationDigest;
23
+ /**
24
+ * @maxItems 1000
25
+ */
26
+ verifiedCallsiteFingerprints: AiVerificationDigest[];
27
+ /**
28
+ * @maxItems 1000
29
+ */
30
+ appliedTransforms: AiVerificationTransform[];
31
+ /**
32
+ * @minItems 1
33
+ * @maxItems 32
34
+ */
35
+ endpointChecks: AiVerificationEndpointCheck[];
36
+ providerCredentialsAbsent: true;
37
+ smokeTraceId: string;
38
+ evidenceDigest: AiVerificationDigest;
39
+ verifiedAt: string;
40
+ }
41
+ export interface AiVerificationTransform {
42
+ callSiteFingerprint: AiVerificationDigest;
43
+ governedBoundaryFingerprint: AiVerificationDigest;
44
+ transformId: AiVerificationIdentifier;
45
+ transformVersion: string;
46
+ beforeSha256: AiVerificationDigest;
47
+ afterSha256: AiVerificationDigest;
48
+ runtimeShimDigest: AiVerificationDigest;
49
+ }
50
+ export interface AiVerificationEndpointCheck {
51
+ name: AiVerificationIdentifier;
52
+ passed: true;
53
+ configurationMappingVersion: string;
54
+ }
@@ -0,0 +1,16 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ /**
5
+ * Safe JSON error body returned by the Deploy and Status Lambdas for API-level 4xx and 5xx responses. It is distinct from the internal pipeline ErrorEnvelope, which requires a version and evidence reference.
6
+ */
7
+ export interface ApiErrorResponse {
8
+ code: string;
9
+ message: string;
10
+ correlationId: string;
11
+ requestId?: string;
12
+ /**
13
+ * Present when a version was minted before the API-level failure.
14
+ */
15
+ versionId?: string;
16
+ }
@@ -0,0 +1,81 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ /**
5
+ * The build output contract (LLD §3) — the provider-portability seam. Emitted by the build (`artifact.json`) and consumed by every deploy stage below build. Deploy stages read ONLY this document plus the blobs it references. Secret REFERENCES only — never values.
6
+ */
7
+ export interface ArtifactContract {
8
+ contract_version: 1;
9
+ app_id: string;
10
+ commit: string;
11
+ /**
12
+ * sha256 of the canonical bundle; the immutable identity that promotion pins to (invariant §1.3.2).
13
+ */
14
+ artifact_digest: string;
15
+ backend: {
16
+ type: 'oci-image' | 'zip';
17
+ /**
18
+ * ECR uri@digest (oci-image) or S3 key (zip).
19
+ */
20
+ ref: string;
21
+ entrypoint: string;
22
+ runtime: string;
23
+ };
24
+ static: {
25
+ /**
26
+ * e.g. builds/<digest>/
27
+ */
28
+ path: string;
29
+ /**
30
+ * true when static assets carry no env-specific values (no static rebuild needed on promotion).
31
+ */
32
+ env_agnostic: boolean;
33
+ };
34
+ routes: {
35
+ path: string;
36
+ target: 'backend' | 'static';
37
+ }[];
38
+ env: {
39
+ /**
40
+ * Non-secret env vars, e.g. {"PORT":"8080"}.
41
+ */
42
+ vars: {
43
+ [k: string]: string;
44
+ };
45
+ /**
46
+ * Secrets Manager references only, e.g. harbour/<org>/<app>/<env>/STRIPE_KEY. Never values.
47
+ */
48
+ secret_refs: string[];
49
+ };
50
+ storage_bindings: {
51
+ kind: 'sql' | 'kv' | 'files';
52
+ /**
53
+ * Logical name, e.g. "default".
54
+ */
55
+ logical?: string;
56
+ /**
57
+ * e.g. aurora-sv2.
58
+ */
59
+ provider: string;
60
+ }[];
61
+ /**
62
+ * Post-MVP (migration tooling is post-MVP, LLD §4.6); MVP emits an empty array.
63
+ */
64
+ migrations: {
65
+ seq: number;
66
+ sha: string;
67
+ destructive: boolean;
68
+ path: string;
69
+ }[];
70
+ /**
71
+ * MVP emits an empty array.
72
+ */
73
+ scheduled_tasks: {}[];
74
+ transform_report: {
75
+ /**
76
+ * Transformed surfaces, e.g. ["secrets","storage.sql"].
77
+ */
78
+ surfaces: string[];
79
+ static_rebuild_required: boolean;
80
+ };
81
+ }
@@ -0,0 +1,44 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ import { createHash } from 'node:crypto';
5
+ import type { BindingClassificationSubject } from './binding-classification-subject.js';
6
+
7
+ const domain = 'harbour.binding-classification-subject/1.0';
8
+ const token = (value: string): string => Buffer.byteLength(value, 'utf8') + ':' + value;
9
+ const record = (tag: string, values: string[]): string => tag + values.map(token).join('');
10
+
11
+ /** Exact source-revision digest preimage shared by the producer and DCP. */
12
+ export function sourceRevisionPreimage(sourceRef: string, sourceRevision: string): string { return [record('D', ['harbour.source-revision/1.0']), record('S', [sourceRef, sourceRevision])].join('\n'); }
13
+
14
+ export function computeSourceRevisionDigest(sourceRef: string, sourceRevision: string): string { return 'sha256:' + createHash('sha256').update(sourceRevisionPreimage(sourceRef, sourceRevision), 'utf8').digest('hex'); }
15
+
16
+ /** Rejects an unknown or provider-mismatched requested AI mapping. JSON Schema owns field presence and bounds. */
17
+ export function assertBindingClassificationMappings(input: BindingClassificationSubject): void {
18
+ if (input.sourceRevisionDigest !== computeSourceRevisionDigest(input.sourceRef, input.sourceRevision)) throw new Error('sourceRevisionDigest mismatch');
19
+ for (const [name, entry] of Object.entries(input.classifications)) {
20
+ if (entry.kind !== 'provider_credential') continue;
21
+ const mapping = entry.requestedAiMappingId ? input.requestedAiMappings[entry.requestedAiMappingId] : undefined;
22
+ if (!mapping) throw new Error('unknown requestedAiMappingId for ' + name);
23
+ if (mapping.providerId !== entry.providerId) throw new Error('providerId mismatch for ' + name);
24
+ }
25
+ }
26
+
27
+ /** Proves the subject classifies every declared binding exactly once and no undeclared name. */
28
+ export function assertBindingClassificationCoverage(input: BindingClassificationSubject, bindingNames: readonly string[]): void {
29
+ const expected = [...bindingNames].sort();
30
+ if (new Set(expected).size !== expected.length) throw new Error('duplicate declared binding name');
31
+ const actual = Object.keys(input.classifications).sort();
32
+ if (expected.length !== actual.length || expected.some((name, index) => name !== actual[index])) throw new Error('binding classification coverage mismatch');
33
+ }
34
+
35
+ /** Exact UTF-8 length-prefixed preimage. Maps are sorted by key; classificationDigest is excluded. */
36
+ export function bindingClassificationPreimage(input: BindingClassificationSubject): string {
37
+ assertBindingClassificationMappings(input);
38
+ const rows = [record('D', [domain]), record('H', [input.schema, input.status, input.tenantId, input.appId, input.environment, input.sourceRef, input.sourceRevision, input.sourceRevisionDigest, input.classifierRevision])];
39
+ for (const name of Object.keys(input.classifications).sort()) { const entry = input.classifications[name]!; rows.push(record('C', [name, entry.kind, entry.confidence, entry.providerId ?? '', entry.category ?? '', entry.requestedAiMappingId ?? ''])); }
40
+ for (const mappingId of Object.keys(input.requestedAiMappings).sort()) { const mapping = input.requestedAiMappings[mappingId]!; rows.push(record('M', [mappingId, mapping.mappingVersion, mapping.providerId])); }
41
+ return rows.join('\n');
42
+ }
43
+
44
+ export function computeBindingClassificationDigest(input: BindingClassificationSubject): string { return 'sha256:' + createHash('sha256').update(bindingClassificationPreimage(input), 'utf8').digest('hex'); }
@@ -0,0 +1,60 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ export type BindingClassificationIdentifier = string;
5
+ /**
6
+ * 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.
7
+ */
8
+ export type BindingClassificationSourceRef = string;
9
+ /**
10
+ * 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.
11
+ */
12
+ export type BindingClassificationSourceRevision = string;
13
+ export type BindingClassificationDigest = string;
14
+ export type BindingClassificationEntry = {
15
+ kind: 'configuration' | 'secret' | 'provider_credential';
16
+ confidence: 'high' | 'low';
17
+ providerId?: BindingClassificationProviderId;
18
+ category?: BindingClassificationCredentialCategory;
19
+ requestedAiMappingId?: BindingClassificationIdentifier;
20
+ };
21
+ export type BindingClassificationProviderId = 'openai' | 'anthropic' | 'gemini' | 'openrouter';
22
+ /**
23
+ * Closed current credential category. Add a reviewed contract version before supporting another credential mechanism.
24
+ */
25
+ export type BindingClassificationCredentialCategory = 'api_key';
26
+
27
+ /**
28
+ * 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.
29
+ */
30
+ export interface BindingClassificationSubject {
31
+ schema: 'harbour.binding-classification-subject/1.0';
32
+ /**
33
+ * Partial or unavailable classification cannot be represented as an admissible subject.
34
+ */
35
+ status: 'COMPLETE';
36
+ tenantId: BindingClassificationIdentifier;
37
+ appId: BindingClassificationIdentifier;
38
+ environment: 'preview' | 'prod';
39
+ sourceRef: BindingClassificationSourceRef;
40
+ sourceRevision: BindingClassificationSourceRevision;
41
+ sourceRevisionDigest: BindingClassificationDigest;
42
+ classifierRevision: string;
43
+ /**
44
+ * 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.
45
+ */
46
+ classifications: {
47
+ [k: string]: BindingClassificationEntry;
48
+ };
49
+ /**
50
+ * 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.
51
+ */
52
+ requestedAiMappings: {
53
+ [k: string]: BindingClassificationAiMapping;
54
+ };
55
+ classificationDigest: BindingClassificationDigest;
56
+ }
57
+ export interface BindingClassificationAiMapping {
58
+ mappingVersion: string;
59
+ providerId: BindingClassificationProviderId;
60
+ }
@@ -0,0 +1,10 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ export const ContractRepositoryVersion = "0.10.0.0" as const;
5
+ export const ContractPublishVersion = "0.10.0" as const;
6
+ export const ContractPublishTag = "v0.10.0" as const;
7
+ export const ContractSchemasDigest = "sha256:e22fd6329b9064a3d653dbc6bc3ee0c2033ee6d8d279f06532a355c9285aa7fe" as const;
8
+ export const ContractAiFixturesDigest = "sha256:8355039f14457087d36aee52ae1a0a93096bf12f34bd8af0d948c605b6dff226" as const;
9
+ export const ContractGeneratedGoDigest = "sha256:d1a57e9b7b414bc239af4dd897c444bfac08e3bfbb24426d525faad6878d6a26" as const;
10
+ export const ContractGeneratedTypeScriptDigest = "sha256:e455c76468808b1191a3d2d774786bb18dc435785d9fa31a559f6b93f3e160c9" as const;
@@ -0,0 +1,46 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ /**
5
+ * database = a SQL/warehouse connection; api = an HTTP(S) service; app_owned_external = infrastructure the app owns outside Harbour.
6
+ */
7
+ export type DataDependencyKind = 'database' | 'api' | 'app_owned_external';
8
+ /**
9
+ * matched = resolved to a registered source; out_of_scope = resolved but not requestable; unknown = unresolvable; inventory = registered but unused; app_owned_external = app-owned, outside Harbour's remit.
10
+ */
11
+ export type DataDependencyClassification = 'matched' | 'out_of_scope' | 'unknown' | 'inventory' | 'app_owned_external';
12
+ /**
13
+ * declared = stated by the builder; detected = found by analysis.
14
+ */
15
+ export type DataDependencyOrigin = 'declared' | 'detected';
16
+
17
+ /**
18
+ * A single external data dependency of an app, either declared by the builder or detected during analysis. Classification records how the dependency resolved against the tenant's registered data-source inventory; matchedSourceId is present only when classification is matched.
19
+ */
20
+ export interface DataDependency {
21
+ kind: DataDependencyKind;
22
+ identifiers: DataDependencyIdentifiers;
23
+ classification: DataDependencyClassification;
24
+ /**
25
+ * sourceId of the registered data source this dependency matched; present only when classification is matched.
26
+ */
27
+ matchedSourceId?: string;
28
+ origin: DataDependencyOrigin;
29
+ }
30
+ /**
31
+ * Evidence identifying the dependency; used to match it against the registered source inventory.
32
+ */
33
+ export interface DataDependencyIdentifiers {
34
+ /**
35
+ * Hostnames the dependency was observed or declared against.
36
+ */
37
+ hosts: string[];
38
+ /**
39
+ * Client driver/library that surfaced the dependency (e.g. pg, snowflake-sdk).
40
+ */
41
+ driver?: string;
42
+ /**
43
+ * Environment-variable names the app reads to configure this dependency.
44
+ */
45
+ envNames: string[];
46
+ }
@@ -0,0 +1,71 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ /**
5
+ * Access mode. MVP is read-only; write is a deliberate future schema change.
6
+ */
7
+ export type DataGrantMode = 'read';
8
+ /**
9
+ * MVP: one minted service principal per grant. Per-end-user passthrough is a future schema change.
10
+ */
11
+ export type DataGrantIdentityModel = 'service';
12
+ /**
13
+ * Grant lifecycle state. Legal edges live in x-transitions; GRANTED and DENIED are terminal.
14
+ */
15
+ export type DataGrantStatus = 'REQUESTED' | 'GRANTED' | 'DENIED';
16
+
17
+ /**
18
+ * An app's approved (or requested) read scope against a registered data source. MVP is deliberately narrow: mode is read-only and identityModel is service (one minted principal per grant, no per-end-user identity passthrough). Grant status is a tiny state machine — legal edges live in x-transitions and codegen turns them into shared CanTransitionDataGrantStatus / IsTerminalDataGrantStatus helpers so control-plane and consumers agree on legality.
19
+ */
20
+ export interface DataGrant {
21
+ /**
22
+ * App the grant belongs to.
23
+ */
24
+ appId: string;
25
+ /**
26
+ * Registered data source the grant targets (data-source.schema.json).
27
+ */
28
+ sourceId: string;
29
+ /**
30
+ * Requested/granted selectors; every selector must fall inside the source's allowedScope.
31
+ *
32
+ * @minItems 1
33
+ */
34
+ scope: string[];
35
+ mode: DataGrantMode;
36
+ identityModel: DataGrantIdentityModel;
37
+ status: DataGrantStatus;
38
+ /**
39
+ * secretref URI of the minted per-grant credential; present once GRANTED.
40
+ */
41
+ credentialRef?: string;
42
+ /**
43
+ * Name of the scoped principal minted on the source for this grant; present once GRANTED.
44
+ */
45
+ mintedPrincipal?: string;
46
+ /**
47
+ * Operation (e.g. deployment analysis) that raised the request, for audit correlation.
48
+ */
49
+ requestedByOperationId?: string;
50
+ /**
51
+ * Admin identity that resolved the request; present once GRANTED or DENIED.
52
+ */
53
+ approvedBy?: string;
54
+ /**
55
+ * RFC 3339 instant the request was resolved; present once GRANTED or DENIED.
56
+ */
57
+ approvedAt?: string;
58
+ }
59
+
60
+ const legalDataGrantStatusTransitions: Record<string, Record<string, true>> = {
61
+ "REQUESTED": { "GRANTED": true, "DENIED": true },
62
+ };
63
+ const terminalDataGrantStatusStates = new Set<string>(["GRANTED", "DENIED"]);
64
+
65
+ /** True iff from→to is a legal DataGrantStatus edge (schema x-transitions). */
66
+ export function canTransitionDataGrantStatus(from: string, to: string): boolean {
67
+ return Boolean(legalDataGrantStatusTransitions[from]?.[to]);
68
+ }
69
+
70
+ /** True iff the DataGrantStatus is terminal (GRANTED, DENIED). */
71
+ export function isTerminalDataGrantStatus(state: string): boolean { return terminalDataGrantStatusStates.has(state); }
@@ -0,0 +1,78 @@
1
+ /* eslint-disable */
2
+ // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
+
4
+ /**
5
+ * Closed connector set for the Data-Access MVP.
6
+ */
7
+ export type DataSourceConnector = 'postgres' | 'redshift' | 'bigquery' | 'databricks';
8
+ /**
9
+ * byo = admin brought existing infrastructure; harbour_managed = Harbour provisioned it.
10
+ */
11
+ export type DataSourceProvisioningMode = 'byo' | 'harbour_managed';
12
+ /**
13
+ * DISABLED sources reject new grants and suspend existing access.
14
+ */
15
+ export type DataSourceStatus = 'ACTIVE' | 'DISABLED';
16
+
17
+ /**
18
+ * An admin-registered external data source (Data-Access MVP). The credential the platform holds for the source never crosses this boundary — credentialRef is a secretref URI resolved internally. allowedScope is the admin-declared selector allowlist grants are checked against; observedScope is what discovery actually saw and is informational only.
19
+ */
20
+ export interface DataSource {
21
+ /**
22
+ * Owning tenant (org) identifier.
23
+ */
24
+ tenantId: string;
25
+ /**
26
+ * Tenant-unique data-source identifier.
27
+ */
28
+ sourceId: string;
29
+ connector: DataSourceConnector;
30
+ /**
31
+ * Human-readable name shown to admins and builders.
32
+ */
33
+ displayName: string;
34
+ endpoint: DataSourceEndpoint;
35
+ /**
36
+ * secretref URI naming the platform-held credential for this source. The referenced value never crosses this boundary.
37
+ */
38
+ credentialRef: string;
39
+ provisioningMode: DataSourceProvisioningMode;
40
+ /**
41
+ * Admin-declared selector allowlist (e.g. schema.table patterns) that grant scopes are validated against.
42
+ */
43
+ allowedScope: string[];
44
+ /**
45
+ * Selectors discovery actually observed on the source. Informational; never widens allowedScope.
46
+ */
47
+ observedScope?: string[];
48
+ /**
49
+ * Whether builders may request grants against this source.
50
+ */
51
+ requestableByBuilders: boolean;
52
+ status: DataSourceStatus;
53
+ }
54
+ /**
55
+ * Connector endpoint coordinates. host is always present; database vs project/workspace depends on the connector family.
56
+ */
57
+ export interface DataSourceEndpoint {
58
+ /**
59
+ * Network host (or warehouse endpoint) of the source.
60
+ */
61
+ host: string;
62
+ /**
63
+ * TCP port; absent where the connector implies it.
64
+ */
65
+ port?: number;
66
+ /**
67
+ * Database name (postgres/redshift-style connectors).
68
+ */
69
+ database?: string;
70
+ /**
71
+ * Project identifier (bigquery-style connectors).
72
+ */
73
+ project?: string;
74
+ /**
75
+ * Workspace identifier (databricks-style connectors).
76
+ */
77
+ workspace?: string;
78
+ }