@fourier-labs/harbour-contracts 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -0
- package/dist/api-error-response.d.ts +13 -0
- package/dist/api-error-response.js +3 -0
- package/dist/artifact-contract.d.ts +78 -0
- package/dist/artifact-contract.js +3 -0
- package/dist/deploy-request.d.ts +32 -0
- package/dist/deploy-request.js +3 -0
- package/dist/deploy-response.d.ts +22 -0
- package/dist/deploy-response.js +3 -0
- package/dist/deployment-environment.d.ts +4 -0
- package/dist/deployment-environment.js +3 -0
- package/dist/deployment-source.d.ts +13 -0
- package/dist/deployment-source.js +3 -0
- package/dist/deployment-status-failure.d.ts +9 -0
- package/dist/deployment-status-failure.js +3 -0
- package/dist/deployment-status-request.d.ts +8 -0
- package/dist/deployment-status-request.js +3 -0
- package/dist/deployment-status-response.d.ts +24 -0
- package/dist/deployment-status-response.js +3 -0
- package/dist/env-bindings.d.ts +61 -0
- package/dist/env-bindings.js +3 -0
- package/dist/error-category-registry.d.ts +33 -0
- package/dist/error-category-registry.js +85 -0
- package/dist/error-category.d.ts +4 -0
- package/dist/error-category.js +3 -0
- package/dist/error-envelope.d.ts +46 -0
- package/dist/error-envelope.js +3 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +18 -0
- package/dist/kvs-routing-entry.d.ts +13 -0
- package/dist/kvs-routing-entry.js +3 -0
- package/dist/version-state-machine.d.ts +19 -0
- package/dist/version-state-machine.js +35 -0
- package/dist/version-state.d.ts +4 -0
- package/dist/version-state.js +3 -0
- package/package.json +30 -0
- package/schema/api-error-response.schema.json +33 -0
- package/schema/artifact-contract.schema.json +126 -0
- package/schema/deploy-request.schema.json +68 -0
- package/schema/deploy-response.schema.json +49 -0
- package/schema/deployment-environment.schema.json +9 -0
- package/schema/deployment-source.schema.json +29 -0
- package/schema/deployment-status-failure.schema.json +28 -0
- package/schema/deployment-status-request.schema.json +25 -0
- package/schema/deployment-status-response.schema.json +79 -0
- package/schema/env-bindings.schema.json +62 -0
- package/schema/error-category.enum.json +49 -0
- package/schema/error-envelope.schema.json +85 -0
- package/schema/kvs-routing-entry.schema.json +21 -0
- package/schema/version-state.schema.json +66 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Value stored in the CloudFront KeyValueStore, keyed by host header `<app>.(beta.)harbour.dev` and read by the CloudFront Function on every request (LLD §8). MVP carries exactly {static, api} and nothing else — quota/kill-switch fields are a deliberate, additive future extension consumed by the same function.
|
|
3
|
+
*/
|
|
4
|
+
export interface KvsRoutingEntry {
|
|
5
|
+
/**
|
|
6
|
+
* S3 origin path for static assets of the LIVE version, e.g. `builds/<app>/<digest>/` (from the artifact `static.path`, LLD §3).
|
|
7
|
+
*/
|
|
8
|
+
static: string;
|
|
9
|
+
/**
|
|
10
|
+
* Backend target for `/api/*` routes — the Lambda alias reached via API Gateway / Function URL for the LIVE version.
|
|
11
|
+
*/
|
|
12
|
+
api: string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const States: {
|
|
2
|
+
readonly QUEUED: "QUEUED";
|
|
3
|
+
readonly ANALYZING: "ANALYZING";
|
|
4
|
+
readonly TRANSFORMING: "TRANSFORMING";
|
|
5
|
+
readonly VALIDATING: "VALIDATING";
|
|
6
|
+
readonly BUILDING: "BUILDING";
|
|
7
|
+
readonly VERIFYING: "VERIFYING";
|
|
8
|
+
readonly GATING: "GATING";
|
|
9
|
+
readonly LIVE: "LIVE";
|
|
10
|
+
readonly AWAITINGPROMOTION: "AWAITING-PROMOTION";
|
|
11
|
+
readonly PROMOTING: "PROMOTING";
|
|
12
|
+
readonly FAILED: "FAILED";
|
|
13
|
+
readonly STALE: "STALE";
|
|
14
|
+
};
|
|
15
|
+
export type StateName = (typeof States)[keyof typeof States];
|
|
16
|
+
/** True iff from→to is a legal edge in the T1a state machine. */
|
|
17
|
+
export declare function canTransition(from: string, to: string): boolean;
|
|
18
|
+
/** True iff the state is terminal (FAILED, STALE). */
|
|
19
|
+
export declare function isTerminal(state: string): boolean;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
|
+
export const States = {
|
|
4
|
+
QUEUED: "QUEUED",
|
|
5
|
+
ANALYZING: "ANALYZING",
|
|
6
|
+
TRANSFORMING: "TRANSFORMING",
|
|
7
|
+
VALIDATING: "VALIDATING",
|
|
8
|
+
BUILDING: "BUILDING",
|
|
9
|
+
VERIFYING: "VERIFYING",
|
|
10
|
+
GATING: "GATING",
|
|
11
|
+
LIVE: "LIVE",
|
|
12
|
+
AWAITINGPROMOTION: "AWAITING-PROMOTION",
|
|
13
|
+
PROMOTING: "PROMOTING",
|
|
14
|
+
FAILED: "FAILED",
|
|
15
|
+
STALE: "STALE",
|
|
16
|
+
};
|
|
17
|
+
const legalTransitions = {
|
|
18
|
+
"QUEUED": { "ANALYZING": true, "FAILED": true, "STALE": true },
|
|
19
|
+
"ANALYZING": { "BUILDING": true, "TRANSFORMING": true, "FAILED": true, "STALE": true },
|
|
20
|
+
"TRANSFORMING": { "VALIDATING": true, "FAILED": true, "STALE": true },
|
|
21
|
+
"VALIDATING": { "BUILDING": true, "TRANSFORMING": true, "FAILED": true, "STALE": true },
|
|
22
|
+
"BUILDING": { "VERIFYING": true, "FAILED": true, "STALE": true },
|
|
23
|
+
"VERIFYING": { "GATING": true, "FAILED": true, "STALE": true },
|
|
24
|
+
"GATING": { "LIVE": true, "FAILED": true, "STALE": true },
|
|
25
|
+
"LIVE": { "AWAITING-PROMOTION": true },
|
|
26
|
+
"AWAITING-PROMOTION": { "PROMOTING": true },
|
|
27
|
+
"PROMOTING": { "LIVE": true, "FAILED": true },
|
|
28
|
+
};
|
|
29
|
+
const terminalStates = new Set(["FAILED", "STALE"]);
|
|
30
|
+
/** True iff from→to is a legal edge in the T1a state machine. */
|
|
31
|
+
export function canTransition(from, to) {
|
|
32
|
+
return Boolean(legalTransitions[from] && legalTransitions[from][to]);
|
|
33
|
+
}
|
|
34
|
+
/** True iff the state is terminal (FAILED, STALE). */
|
|
35
|
+
export function isTerminal(state) { return terminalStates.has(state); }
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 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.
|
|
3
|
+
*/
|
|
4
|
+
export type VersionState = 'QUEUED' | 'ANALYZING' | 'TRANSFORMING' | 'VALIDATING' | 'BUILDING' | 'VERIFYING' | 'GATING' | 'LIVE' | 'AWAITING-PROMOTION' | 'PROMOTING' | 'FAILED' | 'STALE';
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fourier-labs/harbour-contracts",
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Single source of truth for every data shape that crosses a boundary between Harbour components.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"private": false,
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Fourier-Labs-AI/harbour-contracts.git"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"schema"
|
|
15
|
+
],
|
|
16
|
+
"main": "dist/index.js",
|
|
17
|
+
"types": "dist/index.d.ts",
|
|
18
|
+
"scripts": {
|
|
19
|
+
"gen": "node tools/gen/gen.mjs",
|
|
20
|
+
"build": "tsc -p tsconfig.json",
|
|
21
|
+
"test": "node --test tools/gen/*.test.mjs",
|
|
22
|
+
"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; }; }"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"ajv": "^8.17.1",
|
|
26
|
+
"ajv-formats": "^3.0.1",
|
|
27
|
+
"json-schema-to-typescript": "^15.0.4",
|
|
28
|
+
"typescript": "^5.7.2"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.harbour.dev/api-error-response.schema.json",
|
|
4
|
+
"title": "ApiErrorResponse",
|
|
5
|
+
"description": "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
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["code", "message", "correlationId"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"code": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^[a-z][a-z0-9]*(\\.[a-z0-9-]+)+$"
|
|
13
|
+
},
|
|
14
|
+
"message": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"minLength": 1,
|
|
17
|
+
"maxLength": 500
|
|
18
|
+
},
|
|
19
|
+
"correlationId": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"minLength": 1,
|
|
22
|
+
"maxLength": 128
|
|
23
|
+
},
|
|
24
|
+
"requestId": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"maxLength": 128
|
|
27
|
+
},
|
|
28
|
+
"versionId": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"description": "Present when a version was minted before the API-level failure."
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.harbour.dev/artifact-contract.schema.json",
|
|
4
|
+
"title": "ArtifactContract",
|
|
5
|
+
"description": "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
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": [
|
|
9
|
+
"contract_version",
|
|
10
|
+
"app_id",
|
|
11
|
+
"commit",
|
|
12
|
+
"artifact_digest",
|
|
13
|
+
"backend",
|
|
14
|
+
"static",
|
|
15
|
+
"routes",
|
|
16
|
+
"env",
|
|
17
|
+
"storage_bindings",
|
|
18
|
+
"migrations",
|
|
19
|
+
"scheduled_tasks",
|
|
20
|
+
"transform_report"
|
|
21
|
+
],
|
|
22
|
+
"properties": {
|
|
23
|
+
"contract_version": { "type": "integer", "const": 1 },
|
|
24
|
+
"app_id": { "type": "string", "minLength": 1 },
|
|
25
|
+
"commit": { "type": "string", "pattern": "^[0-9a-f]{7,40}$" },
|
|
26
|
+
"artifact_digest": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"pattern": "^(sha256:)?[0-9a-f]{64}$",
|
|
29
|
+
"description": "sha256 of the canonical bundle; the immutable identity that promotion pins to (invariant §1.3.2)."
|
|
30
|
+
},
|
|
31
|
+
"backend": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"additionalProperties": false,
|
|
34
|
+
"required": ["type", "ref", "entrypoint", "runtime"],
|
|
35
|
+
"properties": {
|
|
36
|
+
"type": { "type": "string", "enum": ["oci-image", "zip"] },
|
|
37
|
+
"ref": { "type": "string", "minLength": 1, "description": "ECR uri@digest (oci-image) or S3 key (zip)." },
|
|
38
|
+
"entrypoint": { "type": "string", "minLength": 1 },
|
|
39
|
+
"runtime": { "type": "string", "minLength": 1 }
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"static": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"additionalProperties": false,
|
|
45
|
+
"required": ["path", "env_agnostic"],
|
|
46
|
+
"properties": {
|
|
47
|
+
"path": { "type": "string", "minLength": 1, "description": "e.g. builds/<digest>/" },
|
|
48
|
+
"env_agnostic": { "type": "boolean", "description": "true when static assets carry no env-specific values (no static rebuild needed on promotion)." }
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"routes": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"items": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"additionalProperties": false,
|
|
56
|
+
"required": ["path", "target"],
|
|
57
|
+
"properties": {
|
|
58
|
+
"path": { "type": "string", "minLength": 1 },
|
|
59
|
+
"target": { "type": "string", "enum": ["backend", "static"] }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"env": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"additionalProperties": false,
|
|
66
|
+
"required": ["vars", "secret_refs"],
|
|
67
|
+
"properties": {
|
|
68
|
+
"vars": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"additionalProperties": { "type": "string" },
|
|
71
|
+
"description": "Non-secret env vars, e.g. {\"PORT\":\"8080\"}."
|
|
72
|
+
},
|
|
73
|
+
"secret_refs": {
|
|
74
|
+
"type": "array",
|
|
75
|
+
"items": { "type": "string", "pattern": "^harbour/" },
|
|
76
|
+
"description": "Secrets Manager references only, e.g. harbour/<org>/<app>/<env>/STRIPE_KEY. Never values."
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"storage_bindings": {
|
|
81
|
+
"type": "array",
|
|
82
|
+
"items": {
|
|
83
|
+
"type": "object",
|
|
84
|
+
"required": ["kind", "provider"],
|
|
85
|
+
"properties": {
|
|
86
|
+
"kind": { "type": "string", "enum": ["sql", "kv", "files"] },
|
|
87
|
+
"logical": { "type": "string", "description": "Logical name, e.g. \"default\"." },
|
|
88
|
+
"provider": { "type": "string", "description": "e.g. aurora-sv2." }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"migrations": {
|
|
93
|
+
"type": "array",
|
|
94
|
+
"description": "Post-MVP (migration tooling is post-MVP, LLD §4.6); MVP emits an empty array.",
|
|
95
|
+
"items": {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"additionalProperties": false,
|
|
98
|
+
"required": ["seq", "sha", "destructive", "path"],
|
|
99
|
+
"properties": {
|
|
100
|
+
"seq": { "type": "integer", "minimum": 0 },
|
|
101
|
+
"sha": { "type": "string" },
|
|
102
|
+
"destructive": { "type": "boolean" },
|
|
103
|
+
"path": { "type": "string" }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"scheduled_tasks": {
|
|
108
|
+
"type": "array",
|
|
109
|
+
"description": "MVP emits an empty array.",
|
|
110
|
+
"items": { "type": "object" }
|
|
111
|
+
},
|
|
112
|
+
"transform_report": {
|
|
113
|
+
"type": "object",
|
|
114
|
+
"additionalProperties": false,
|
|
115
|
+
"required": ["surfaces", "static_rebuild_required"],
|
|
116
|
+
"properties": {
|
|
117
|
+
"surfaces": {
|
|
118
|
+
"type": "array",
|
|
119
|
+
"items": { "type": "string" },
|
|
120
|
+
"description": "Transformed surfaces, e.g. [\"secrets\",\"storage.sql\"]."
|
|
121
|
+
},
|
|
122
|
+
"static_rebuild_required": { "type": "boolean" }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.harbour.dev/deploy-request.schema.json",
|
|
4
|
+
"title": "DeployRequest",
|
|
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
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["source", "environment", "branch", "commit"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"source": {
|
|
11
|
+
"$ref": "#/definitions/DeployRequestSource"
|
|
12
|
+
},
|
|
13
|
+
"environment": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"enum": ["preview"],
|
|
16
|
+
"description": "Deployment target. The API rejects production; production remains human-merge gated."
|
|
17
|
+
},
|
|
18
|
+
"branch": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"minLength": 1,
|
|
21
|
+
"maxLength": 255,
|
|
22
|
+
"description": "Git branch that must contain the exact requested commit."
|
|
23
|
+
},
|
|
24
|
+
"commit": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"pattern": "^[0-9a-f]{7,40}$",
|
|
27
|
+
"description": "Exact lowercase hexadecimal Git commit to materialize and deploy."
|
|
28
|
+
},
|
|
29
|
+
"idempotencyKey": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$",
|
|
32
|
+
"description": "Optional legacy request-correlation value returned as requestId. It does not make repeated HTTP POSTs idempotent; versionId is the deployment identity and SQS deduplication key."
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"definitions": {
|
|
36
|
+
"DeployRequestSource": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"additionalProperties": false,
|
|
39
|
+
"required": ["provider", "repositoryUrl"],
|
|
40
|
+
"properties": {
|
|
41
|
+
"provider": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"enum": ["github"],
|
|
44
|
+
"description": "Source provider. The preview MVP accepts only github."
|
|
45
|
+
},
|
|
46
|
+
"repositoryUrl": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"minLength": 20,
|
|
49
|
+
"maxLength": 300,
|
|
50
|
+
"pattern": "^https://github\\.com/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:\\.git)?/?$",
|
|
51
|
+
"description": "Credential-free HTTPS GitHub repository URL."
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"examples": [
|
|
57
|
+
{
|
|
58
|
+
"source": {
|
|
59
|
+
"provider": "github",
|
|
60
|
+
"repositoryUrl": "https://github.com/Fourier-Labs-AI/focus-list"
|
|
61
|
+
},
|
|
62
|
+
"environment": "preview",
|
|
63
|
+
"branch": "preview-deployment",
|
|
64
|
+
"commit": "abc1234567890abcdef1234567890abcdef12345",
|
|
65
|
+
"idempotencyKey": "deploy-attempt-123"
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.harbour.dev/deploy-response.schema.json",
|
|
4
|
+
"title": "DeployResponse",
|
|
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
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["requestId", "versionId", "tenant", "app", "environment", "status", "acceptedAt", "statusUrl"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"requestId": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"minLength": 1,
|
|
13
|
+
"maxLength": 128,
|
|
14
|
+
"description": "Caller correlation value. This is not the deployment identity."
|
|
15
|
+
},
|
|
16
|
+
"versionId": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"pattern": "^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?#[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?#preview#[0-9]{6}$",
|
|
19
|
+
"description": "Immutable identity of the admitted preview version."
|
|
20
|
+
},
|
|
21
|
+
"tenant": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"minLength": 1,
|
|
24
|
+
"maxLength": 63
|
|
25
|
+
},
|
|
26
|
+
"app": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"minLength": 1,
|
|
29
|
+
"maxLength": 63
|
|
30
|
+
},
|
|
31
|
+
"environment": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"enum": ["preview"]
|
|
34
|
+
},
|
|
35
|
+
"status": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"enum": ["QUEUED"]
|
|
38
|
+
},
|
|
39
|
+
"acceptedAt": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"format": "date-time"
|
|
42
|
+
},
|
|
43
|
+
"statusUrl": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"pattern": "^/v1/tenants/[^/]+/apps/[^/]+/deployments/[^/]+$",
|
|
46
|
+
"description": "Relative URL for exact-version Status. The versionId path segment is URL encoded."
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.harbour.dev/deployment-environment.schema.json",
|
|
4
|
+
"title": "DeploymentEnvironment",
|
|
5
|
+
"description": "Public environment accepted by the preview deployment-control-plane APIs. The MVP accepts only preview; production remains reachable only through Harbour's human-merge promotion flow.",
|
|
6
|
+
"type": "string",
|
|
7
|
+
"enum": ["preview"],
|
|
8
|
+
"examples": ["preview"]
|
|
9
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.harbour.dev/deployment-source.schema.json",
|
|
4
|
+
"title": "DeploymentSource",
|
|
5
|
+
"description": "Credential-free GitHub source supplied in the DeployRequest body. Credentials are resolved internally by MaterializeSource and never cross the API boundary.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["provider", "repositoryUrl"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"provider": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"enum": ["github"],
|
|
13
|
+
"description": "Source provider. The preview MVP accepts only GitHub."
|
|
14
|
+
},
|
|
15
|
+
"repositoryUrl": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"minLength": 20,
|
|
18
|
+
"maxLength": 300,
|
|
19
|
+
"pattern": "^https://github\\.com/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:\\.git)?/?$",
|
|
20
|
+
"description": "Credential-free HTTPS GitHub repository URL. Query strings, fragments, embedded credentials and non-github.com hosts are rejected."
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"examples": [
|
|
24
|
+
{
|
|
25
|
+
"provider": "github",
|
|
26
|
+
"repositoryUrl": "https://github.com/Fourier-Labs-AI/focus-list"
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.harbour.dev/deployment-status-failure.schema.json",
|
|
4
|
+
"title": "DeploymentStatusFailure",
|
|
5
|
+
"description": "Frontend-safe failure projection nested in a failed DeploymentStatusResponse. Internal evidence references, AWS identifiers and raw provider errors are excluded.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["category", "retryable", "message", "remediationHint"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"category": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^[a-z][a-z0-9]*(\\.[a-z0-9-]+)+$"
|
|
13
|
+
},
|
|
14
|
+
"retryable": {
|
|
15
|
+
"type": "boolean"
|
|
16
|
+
},
|
|
17
|
+
"message": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"minLength": 1,
|
|
20
|
+
"maxLength": 500
|
|
21
|
+
},
|
|
22
|
+
"remediationHint": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"minLength": 1,
|
|
25
|
+
"maxLength": 1000
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.harbour.dev/deployment-status-request.schema.json",
|
|
4
|
+
"title": "DeploymentStatusRequest",
|
|
5
|
+
"description": "Validated path parameters for GET /v1/tenants/{tenant}/apps/{app}/deployments/{versionId}. The Status Lambda constructs this object from API Gateway path parameters; it is not a GET request body.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["tenant", "app", "versionId"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"tenant": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"minLength": 1,
|
|
13
|
+
"maxLength": 63
|
|
14
|
+
},
|
|
15
|
+
"app": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"minLength": 1,
|
|
18
|
+
"maxLength": 63
|
|
19
|
+
},
|
|
20
|
+
"versionId": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"pattern": "^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?#[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?#preview#[0-9]{6}$"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.harbour.dev/deployment-status-response.schema.json",
|
|
4
|
+
"title": "DeploymentStatusResponse",
|
|
5
|
+
"description": "200 OK body returned by GET /v1/tenants/{tenant}/apps/{app}/deployments/{versionId}. It describes that exact immutable version; newer deployments cannot change which record this response represents.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["requestId", "versionId", "tenant", "app", "environment", "status", "message", "branch", "commit", "requestedAt", "updatedAt"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"requestId": {
|
|
11
|
+
"type": "string"
|
|
12
|
+
},
|
|
13
|
+
"versionId": {
|
|
14
|
+
"type": "string"
|
|
15
|
+
},
|
|
16
|
+
"tenant": {
|
|
17
|
+
"type": "string"
|
|
18
|
+
},
|
|
19
|
+
"app": {
|
|
20
|
+
"type": "string"
|
|
21
|
+
},
|
|
22
|
+
"environment": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"enum": ["preview"]
|
|
25
|
+
},
|
|
26
|
+
"status": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"enum": ["QUEUED", "ANALYZING", "TRANSFORMING", "VALIDATING", "BUILDING", "VERIFYING", "GATING", "LIVE", "FAILED", "STALE"]
|
|
29
|
+
},
|
|
30
|
+
"message": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"minLength": 1,
|
|
33
|
+
"maxLength": 500
|
|
34
|
+
},
|
|
35
|
+
"branch": {
|
|
36
|
+
"type": "string"
|
|
37
|
+
},
|
|
38
|
+
"commit": {
|
|
39
|
+
"type": "string"
|
|
40
|
+
},
|
|
41
|
+
"requestedAt": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"format": "date-time"
|
|
44
|
+
},
|
|
45
|
+
"updatedAt": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"format": "date-time"
|
|
48
|
+
},
|
|
49
|
+
"url": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"format": "uri"
|
|
52
|
+
},
|
|
53
|
+
"failure": {
|
|
54
|
+
"$ref": "#/definitions/StatusFailureProjection"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"definitions": {
|
|
58
|
+
"StatusFailureProjection": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"additionalProperties": false,
|
|
61
|
+
"required": ["category", "retryable", "message", "remediationHint"],
|
|
62
|
+
"properties": {
|
|
63
|
+
"category": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"pattern": "^[a-z][a-z0-9]*(\\.[a-z0-9-]+)+$"
|
|
66
|
+
},
|
|
67
|
+
"retryable": {
|
|
68
|
+
"type": "boolean"
|
|
69
|
+
},
|
|
70
|
+
"message": {
|
|
71
|
+
"type": "string"
|
|
72
|
+
},
|
|
73
|
+
"remediationHint": {
|
|
74
|
+
"type": "string"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.harbour.dev/env-bindings.schema.json",
|
|
4
|
+
"title": "EnvBindings",
|
|
5
|
+
"description": "Resolved environment bindings (LLD §4.2.2). Deliberately COMPLETE — including BYOC/multi-cloud fields MVP does not use (roleArn/externalId absent = act in own account). Resolved once at admission by EnvResolver, stamped onto the version record; every downstream stage reads it from the record and never re-resolves. Adding a field bumps bindingsVersion and touches every consumer — that is the point. `executionCell` is where source-touching stages run (MVP: provider 'harbour').",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["env", "executor", "sql", "kv", "files", "secrets", "serving", "executionCell", "gates", "bindingsVersion"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"env": { "type": "string", "description": "e.g. preview | prod." },
|
|
11
|
+
"executor": { "$ref": "#/definitions/Binding" },
|
|
12
|
+
"sql": { "$ref": "#/definitions/Binding" },
|
|
13
|
+
"kv": { "$ref": "#/definitions/Binding" },
|
|
14
|
+
"files": { "$ref": "#/definitions/Binding" },
|
|
15
|
+
"secrets": { "$ref": "#/definitions/Binding" },
|
|
16
|
+
"serving": { "$ref": "#/definitions/Binding" },
|
|
17
|
+
"executionCell": { "$ref": "#/definitions/Binding" },
|
|
18
|
+
"gates": {
|
|
19
|
+
"type": "array",
|
|
20
|
+
"items": { "type": "string" },
|
|
21
|
+
"description": "Ordered gate names evaluated at GATING."
|
|
22
|
+
},
|
|
23
|
+
"constraints": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"description": "MVP: absent. Future: per-target constraints (region pins, size caps, …).",
|
|
26
|
+
"additionalProperties": true
|
|
27
|
+
},
|
|
28
|
+
"bindingsVersion": {
|
|
29
|
+
"type": "integer",
|
|
30
|
+
"minimum": 1,
|
|
31
|
+
"description": "Stamped onto the version record for audit; bumps when the binding shape changes."
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"definitions": {
|
|
35
|
+
"Target": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"required": ["account", "region"],
|
|
38
|
+
"properties": {
|
|
39
|
+
"account": { "type": "string", "description": "MVP: own account id." },
|
|
40
|
+
"region": { "type": "string", "description": "MVP: us-east-1." },
|
|
41
|
+
"roleArn": { "type": "string", "description": "BYOC cross-account role; absent in MVP = act locally." },
|
|
42
|
+
"externalId": { "type": "string", "description": "BYOC assume-role external id; absent in MVP." }
|
|
43
|
+
},
|
|
44
|
+
"additionalProperties": false
|
|
45
|
+
},
|
|
46
|
+
"Binding": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"required": ["provider", "target"],
|
|
49
|
+
"additionalProperties": false,
|
|
50
|
+
"properties": {
|
|
51
|
+
"provider": { "type": "string" },
|
|
52
|
+
"target": { "$ref": "#/definitions/Target" },
|
|
53
|
+
"config": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"additionalProperties": true,
|
|
56
|
+
"description": "Provider-specific values, resolved at admission and stamped on the version record. MVP (StaticEnvResolver, from CDK stack outputs) e.g. sql: {clusterArn, proxyEndpoint, databaseName}; serving: {distributionId, bucket}. Kept as a named open map rather than loose top-level keys so BOTH generated languages preserve it faithfully — a Go struct silently drops unrecognized top-level keys, a map does not."
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"description": "provider + target + provider-specific `config` (LLD §4.2.2). The binding object itself is closed: a new first-class field is a deliberate schema change plus a bindingsVersion bump, never an ad-hoc key."
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|