@fourier-labs/harbour-contracts 0.43.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/contract-release.json +35 -11
- package/dist/gen-ts/app-integrations-declaration.d.ts +48 -0
- package/dist/gen-ts/app-integrations-declaration.js +3 -0
- package/dist/gen-ts/builder-input-proposal.d.ts +36 -0
- package/dist/gen-ts/builder-input-proposal.js +3 -0
- package/dist/gen-ts/contract-release.d.ts +6 -6
- package/dist/gen-ts/contract-release.js +6 -6
- package/dist/gen-ts/data-grant-v2.d.ts +84 -0
- package/dist/gen-ts/data-grant-v2.js +13 -0
- package/dist/gen-ts/deployment-continuation.d.ts +19 -0
- package/dist/gen-ts/deployment-continuation.js +3 -0
- package/dist/gen-ts/deployment-status-response.d.ts +30 -1
- package/dist/gen-ts/index.d.ts +12 -0
- package/dist/gen-ts/index.js +12 -0
- package/dist/gen-ts/integration-connect-result.d.ts +14 -0
- package/dist/gen-ts/integration-connect-result.js +3 -0
- package/dist/gen-ts/integration-error-details.d.ts +22 -0
- package/dist/gen-ts/integration-error-details.js +3 -0
- package/dist/gen-ts/integration-execute-request.d.ts +30 -0
- package/dist/gen-ts/integration-execute-request.js +3 -0
- package/dist/gen-ts/integration-execute-result.d.ts +23 -0
- package/dist/gen-ts/integration-execute-result.js +3 -0
- package/dist/gen-ts/kit-bundle-manifest.d.ts +32 -0
- package/dist/gen-ts/kit-bundle-manifest.js +3 -0
- package/dist/gen-ts/kit-lock.d.ts +46 -0
- package/dist/gen-ts/kit-lock.js +3 -0
- package/dist/gen-ts/saas-connection.d.ts +73 -0
- package/dist/gen-ts/saas-connection.js +3 -0
- package/dist/gen-ts/transform-parked-result.d.ts +15 -0
- package/dist/gen-ts/transform-parked-result.js +3 -0
- package/gen-ts/app-integrations-declaration.ts +52 -0
- package/gen-ts/builder-input-proposal.ts +40 -0
- package/gen-ts/contract-release.ts +6 -6
- package/gen-ts/data-grant-v2.ts +98 -0
- package/gen-ts/deployment-continuation.ts +22 -0
- package/gen-ts/deployment-status-response.ts +31 -0
- package/gen-ts/index.ts +12 -0
- package/gen-ts/integration-connect-result.ts +21 -0
- package/gen-ts/integration-error-details.ts +47 -0
- package/gen-ts/integration-execute-request.ts +33 -0
- package/gen-ts/integration-execute-result.ts +27 -0
- package/gen-ts/kit-bundle-manifest.ts +36 -0
- package/gen-ts/kit-lock.ts +48 -0
- package/gen-ts/saas-connection.ts +77 -0
- package/gen-ts/transform-parked-result.ts +18 -0
- package/package.json +1 -1
- package/schema/app-integrations-declaration.schema.json +144 -0
- package/schema/builder-input-proposal.schema.json +73 -0
- package/schema/data-grant-v2.schema.json +108 -0
- package/schema/deployment-continuation.schema.json +39 -0
- package/schema/deployment-status-response.schema.json +50 -1
- package/schema/integration-connect-result.schema.json +24 -0
- package/schema/integration-error-details.schema.json +64 -0
- package/schema/integration-execute-request.schema.json +73 -0
- package/schema/integration-execute-result.schema.json +48 -0
- package/schema/kit-bundle-manifest.schema.json +66 -0
- package/schema/kit-lock.schema.json +37 -0
- package/schema/saas-connection.schema.json +115 -0
- package/schema/transform-parked-result.schema.json +26 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The `data` member of a successful execute response `{data, requestId}`; the SDK unwraps `data`. Exactly one of the three operation results: slack.channel.history -> {messages, hasMore} (plain text/IDs/time only, never the provider payload); slack.message.post -> {messageId}; warehouse.view.read -> {rows} (decimal/64-bit values as strings, dates as ISO strings). An unknown send outcome is never a result; it is an error with details.code SEND_OUTCOME_UNKNOWN. Results over 1 MiB are rejected, not truncated.
|
|
6
|
+
*/
|
|
7
|
+
export type IntegrationExecuteResult = {
|
|
8
|
+
/**
|
|
9
|
+
* @maxItems 15
|
|
10
|
+
*/
|
|
11
|
+
messages?: ChannelMessage[];
|
|
12
|
+
hasMore?: boolean;
|
|
13
|
+
messageId?: string;
|
|
14
|
+
/**
|
|
15
|
+
* @maxItems 1000
|
|
16
|
+
*/
|
|
17
|
+
rows?: {
|
|
18
|
+
[k: string]: unknown;
|
|
19
|
+
}[];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export interface ChannelMessage {
|
|
23
|
+
id: string;
|
|
24
|
+
text: string;
|
|
25
|
+
authorId: string;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Digest-pinned image reference <repository>@sha256:<hex>; tags are never accepted.
|
|
6
|
+
*/
|
|
7
|
+
export type ImageDigestReference = string;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Identity of one published development-kit bundle (shipped with the CLI). It pins the app SDK tarball, the local runtime images and the agent-brief fingerprint that a kit-built app was developed against, so the hosted pipeline can prove compatibility before replacing the vendored SDK (see kit-lock.schema.json).
|
|
11
|
+
*/
|
|
12
|
+
export interface KitBundleManifest {
|
|
13
|
+
schema: 'harbour.kit-bundle/1.0';
|
|
14
|
+
/**
|
|
15
|
+
* The CLI package version that published this bundle.
|
|
16
|
+
*/
|
|
17
|
+
kitVersion: string;
|
|
18
|
+
sdk: {
|
|
19
|
+
package: '@harbour/app-sdk';
|
|
20
|
+
/**
|
|
21
|
+
* SHA-256 of the SDK tarball baked into this bundle; the CodeBuild gate compares it with the pipeline's own tarball.
|
|
22
|
+
*/
|
|
23
|
+
tarballSha256: string;
|
|
24
|
+
};
|
|
25
|
+
images: {
|
|
26
|
+
appGateway: ImageDigestReference;
|
|
27
|
+
sessionFixture: ImageDigestReference;
|
|
28
|
+
};
|
|
29
|
+
brief: {
|
|
30
|
+
/**
|
|
31
|
+
* SHA-256 of the agent-brief tar, or the literal unknown when the bundle carried no brief.
|
|
32
|
+
*/
|
|
33
|
+
fingerprint: string;
|
|
34
|
+
};
|
|
35
|
+
declarationSchema: 'harbour.app-integrations/2.0';
|
|
36
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Committed `.harbour/kit.lock.json`: which app and tenant a kit-built app belongs to and the exact bundle it was developed against. The data-plane vendorAppSDK gate reads bundle.sdk.tarballSha256 and fails with kit_bundle_incompatible (naming both digests) when it differs from the baked tarball; apps without a kit.lock keep the existing path.
|
|
6
|
+
*/
|
|
7
|
+
export interface KitLock {
|
|
8
|
+
schema: 'harbour.kit-lock/1.0';
|
|
9
|
+
appId: string;
|
|
10
|
+
tenantId: string;
|
|
11
|
+
bundle: KitBundleManifest;
|
|
12
|
+
createdAt: string;
|
|
13
|
+
updatedAt: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Identity of one published development-kit bundle (shipped with the CLI). It pins the app SDK tarball, the local runtime images and the agent-brief fingerprint that a kit-built app was developed against, so the hosted pipeline can prove compatibility before replacing the vendored SDK (see kit-lock.schema.json).
|
|
17
|
+
*/
|
|
18
|
+
interface KitBundleManifest {
|
|
19
|
+
schema: 'harbour.kit-bundle/1.0';
|
|
20
|
+
/**
|
|
21
|
+
* The CLI package version that published this bundle.
|
|
22
|
+
*/
|
|
23
|
+
kitVersion: string;
|
|
24
|
+
sdk: {
|
|
25
|
+
package: '@harbour/app-sdk';
|
|
26
|
+
/**
|
|
27
|
+
* SHA-256 of the SDK tarball baked into this bundle; the CodeBuild gate compares it with the pipeline's own tarball.
|
|
28
|
+
*/
|
|
29
|
+
tarballSha256: string;
|
|
30
|
+
};
|
|
31
|
+
images: {
|
|
32
|
+
/**
|
|
33
|
+
* Digest-pinned image reference <repository>@sha256:<hex>; tags are never accepted.
|
|
34
|
+
*/
|
|
35
|
+
appGateway: string;
|
|
36
|
+
/**
|
|
37
|
+
* Digest-pinned image reference <repository>@sha256:<hex>; tags are never accepted.
|
|
38
|
+
*/
|
|
39
|
+
sessionFixture: string;
|
|
40
|
+
};
|
|
41
|
+
brief: {
|
|
42
|
+
/**
|
|
43
|
+
* SHA-256 of the agent-brief tar, or the literal unknown when the bundle carried no brief.
|
|
44
|
+
*/
|
|
45
|
+
fingerprint: string;
|
|
46
|
+
};
|
|
47
|
+
declarationSchema: 'harbour.app-integrations/2.0';
|
|
48
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* DISABLED stops every app call immediately (details.code CONNECTION_DISABLED).
|
|
6
|
+
*/
|
|
7
|
+
export type SaasConnectionStatus = 'ACTIVE' | 'DISABLED';
|
|
8
|
+
export type InstallationName = string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Tenant-wide SaaS connection record (control table sk SAASCONN#<sourceId>, same tenant partition as database sources; alias uniqueness is enforced across both). IT maps each environment to a provider installation and logical channel aliases to channel IDs; apps only ever see the aliases. Contains references, never secrets: the client secret lives in Secrets Manager under clientSecretRef and installation tokens under their own token records.
|
|
12
|
+
*/
|
|
13
|
+
export interface SaasConnection {
|
|
14
|
+
/**
|
|
15
|
+
* Tenant-wide logical alias of a connection (governance sourceId) or of a resource under it.
|
|
16
|
+
*/
|
|
17
|
+
sourceId: string;
|
|
18
|
+
kind: 'saas';
|
|
19
|
+
provider: 'slack';
|
|
20
|
+
displayName: string;
|
|
21
|
+
status: SaasConnectionStatus;
|
|
22
|
+
/**
|
|
23
|
+
* Optimistic-concurrency version; every write is conditional on it.
|
|
24
|
+
*/
|
|
25
|
+
version: number;
|
|
26
|
+
clientId: string;
|
|
27
|
+
/**
|
|
28
|
+
* secretref://tenant/{tenantId}/saas/{sourceId}/client
|
|
29
|
+
*/
|
|
30
|
+
clientSecretRef?: string;
|
|
31
|
+
environments: {
|
|
32
|
+
development?: SaasEnvironmentMapping;
|
|
33
|
+
preview?: SaasEnvironmentMapping;
|
|
34
|
+
production?: SaasEnvironmentMapping;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Operations IT allows apps to request on this connection.
|
|
38
|
+
*
|
|
39
|
+
* @minItems 1
|
|
40
|
+
* @maxItems 2
|
|
41
|
+
*/
|
|
42
|
+
operations: ('slack.channel.history' | 'slack.message.post')[];
|
|
43
|
+
developmentPreapproval: DevelopmentPreapproval;
|
|
44
|
+
/**
|
|
45
|
+
* Provider installations keyed by the installation name environments point at.
|
|
46
|
+
*/
|
|
47
|
+
installations?: {
|
|
48
|
+
[k: string]: SaasInstallation;
|
|
49
|
+
};
|
|
50
|
+
createdAt?: string;
|
|
51
|
+
updatedAt?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface SaasEnvironmentMapping {
|
|
54
|
+
installation: InstallationName;
|
|
55
|
+
channels: {
|
|
56
|
+
/**
|
|
57
|
+
* Provider channel ID; only public, non-archived channels are valid.
|
|
58
|
+
*/
|
|
59
|
+
[k: string]: string;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export interface DevelopmentPreapproval {
|
|
63
|
+
/**
|
|
64
|
+
* Auto-approve development requests within the allowlist.
|
|
65
|
+
*/
|
|
66
|
+
enabled: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Development grant duration; default 24.
|
|
69
|
+
*/
|
|
70
|
+
hours: number;
|
|
71
|
+
}
|
|
72
|
+
export interface SaasInstallation {
|
|
73
|
+
tokenRecordId: string;
|
|
74
|
+
workspaceId: string;
|
|
75
|
+
botUserId: string;
|
|
76
|
+
status: 'ACTIVE' | 'RECONNECT_REQUIRED' | 'REVOKED';
|
|
77
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Discriminated result returned by sequential preview after it has durably checkpointed settled transformation work and must stop for builder input. It is not a successful transformation report.
|
|
6
|
+
*/
|
|
7
|
+
export interface TransformParkedResult {
|
|
8
|
+
outcome: 'parked';
|
|
9
|
+
requestId: string;
|
|
10
|
+
checkpoint: {
|
|
11
|
+
schema: 'harbour.transform-checkpoint/1.0';
|
|
12
|
+
key: string;
|
|
13
|
+
sha256: string;
|
|
14
|
+
};
|
|
15
|
+
answerRevision: number;
|
|
16
|
+
remainingActiveMs: number;
|
|
17
|
+
outstandingWork: string;
|
|
18
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.harbour.dev/app-integrations-declaration.schema.json",
|
|
4
|
+
"title": "AppIntegrationsDeclaration",
|
|
5
|
+
"description": "Committed `.harbour/integrations.json`: the app's declared use of tenant-wide integration connections. Keys are logical connection aliases (the governance sourceId); resources are logical names (Slack channel alias, warehouse view name), never IDs, tokens or URLs. The operation set is closed (three operations) and each operation carries a fixed identity mode: slack.channel.history=user, slack.message.post=app, warehouse.view.read=app. A `saas` connection may declare only the Slack operations and a `database` connection only warehouse.view.read. This file also owns the shared IntegrationOperation / IntegrationIdentityMode / IntegrationEnvironment / IntegrationConnectionKind enums referenced by the other integration schemas.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["schema", "connections"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema": { "type": "string", "const": "harbour.app-integrations/2.0" },
|
|
11
|
+
"connections": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"minProperties": 1,
|
|
14
|
+
"maxProperties": 32,
|
|
15
|
+
"propertyNames": { "$ref": "#/definitions/IntegrationAlias" },
|
|
16
|
+
"additionalProperties": { "$ref": "#/definitions/DeclaredConnection" }
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"definitions": {
|
|
20
|
+
"IntegrationAlias": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"pattern": "^[a-z][a-z0-9_-]{0,62}$",
|
|
23
|
+
"description": "Tenant-wide logical alias of a connection (governance sourceId) or of a resource under it."
|
|
24
|
+
},
|
|
25
|
+
"IntegrationConnectionKind": { "type": "string", "enum": ["saas", "database"] },
|
|
26
|
+
"IntegrationOperation": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"enum": ["slack.channel.history", "slack.message.post", "warehouse.view.read"],
|
|
29
|
+
"description": "Closed operation set. Extending it is a contracts change."
|
|
30
|
+
},
|
|
31
|
+
"IntegrationIdentityMode": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"enum": ["app", "user"],
|
|
34
|
+
"description": "Whose authority an operation runs under. Fixed per operation; callers never choose it."
|
|
35
|
+
},
|
|
36
|
+
"IntegrationEnvironment": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"enum": ["development", "preview", "production"],
|
|
39
|
+
"description": "development is the CLI/local kit; preview and production are hosted."
|
|
40
|
+
},
|
|
41
|
+
"ResourceNames": {
|
|
42
|
+
"type": "array",
|
|
43
|
+
"minItems": 1,
|
|
44
|
+
"maxItems": 64,
|
|
45
|
+
"items": { "$ref": "#/definitions/IntegrationAlias" }
|
|
46
|
+
},
|
|
47
|
+
"ViewResource": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"additionalProperties": false,
|
|
50
|
+
"required": ["columns"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"columns": {
|
|
53
|
+
"type": "array",
|
|
54
|
+
"minItems": 1,
|
|
55
|
+
"maxItems": 32,
|
|
56
|
+
"items": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,127}$" }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"UserModeChannelOperation": {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"additionalProperties": false,
|
|
63
|
+
"required": ["identity", "resources"],
|
|
64
|
+
"properties": {
|
|
65
|
+
"identity": { "type": "string", "const": "user" },
|
|
66
|
+
"resources": { "$ref": "#/definitions/ResourceNames" }
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"AppModeChannelOperation": {
|
|
70
|
+
"type": "object",
|
|
71
|
+
"additionalProperties": false,
|
|
72
|
+
"required": ["identity", "resources"],
|
|
73
|
+
"properties": {
|
|
74
|
+
"identity": { "type": "string", "const": "app" },
|
|
75
|
+
"resources": { "$ref": "#/definitions/ResourceNames" }
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"AppModeViewOperation": {
|
|
79
|
+
"type": "object",
|
|
80
|
+
"additionalProperties": false,
|
|
81
|
+
"required": ["identity", "resources"],
|
|
82
|
+
"properties": {
|
|
83
|
+
"identity": { "type": "string", "const": "app" },
|
|
84
|
+
"resources": {
|
|
85
|
+
"type": "object",
|
|
86
|
+
"minProperties": 1,
|
|
87
|
+
"maxProperties": 64,
|
|
88
|
+
"propertyNames": { "$ref": "#/definitions/IntegrationAlias" },
|
|
89
|
+
"additionalProperties": { "$ref": "#/definitions/ViewResource" }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"DeclaredConnection": {
|
|
94
|
+
"type": "object",
|
|
95
|
+
"additionalProperties": false,
|
|
96
|
+
"required": ["kind", "operations"],
|
|
97
|
+
"properties": {
|
|
98
|
+
"kind": { "$ref": "#/definitions/IntegrationConnectionKind" },
|
|
99
|
+
"operations": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"additionalProperties": false,
|
|
102
|
+
"minProperties": 1,
|
|
103
|
+
"properties": {
|
|
104
|
+
"slack.channel.history": { "$ref": "#/definitions/UserModeChannelOperation" },
|
|
105
|
+
"slack.message.post": { "$ref": "#/definitions/AppModeChannelOperation" },
|
|
106
|
+
"warehouse.view.read": { "$ref": "#/definitions/AppModeViewOperation" }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"if": { "properties": { "kind": { "const": "saas" } } },
|
|
111
|
+
"then": { "properties": { "operations": { "not": { "required": ["warehouse.view.read"] } } } },
|
|
112
|
+
"else": {
|
|
113
|
+
"properties": {
|
|
114
|
+
"operations": {
|
|
115
|
+
"not": { "anyOf": [{ "required": ["slack.channel.history"] }, { "required": ["slack.message.post"] }] }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"examples": [
|
|
122
|
+
{
|
|
123
|
+
"schema": "harbour.app-integrations/2.0",
|
|
124
|
+
"connections": {
|
|
125
|
+
"company-slack": {
|
|
126
|
+
"kind": "saas",
|
|
127
|
+
"operations": {
|
|
128
|
+
"slack.channel.history": { "identity": "user", "resources": ["team-updates"] },
|
|
129
|
+
"slack.message.post": { "identity": "app", "resources": ["team-updates"] }
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
"sales-warehouse": {
|
|
133
|
+
"kind": "database",
|
|
134
|
+
"operations": {
|
|
135
|
+
"warehouse.view.read": {
|
|
136
|
+
"identity": "app",
|
|
137
|
+
"resources": { "weekly_sales": { "columns": ["week", "total"] } }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.harbour.dev/builder-input-proposal.schema.json",
|
|
4
|
+
"title": "BuilderInputProposal",
|
|
5
|
+
"description": "Bounded, value-free proposal emitted when sequential preview transformation cannot continue without builder-owned input. The deployment control plane remains authoritative and applies eligibility checks before exposing it.",
|
|
6
|
+
"x-go-embed": true,
|
|
7
|
+
"type": "object",
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"required": ["outcome", "summary", "outstandingWork", "questions"],
|
|
10
|
+
"properties": {
|
|
11
|
+
"outcome": { "const": "needs_input" },
|
|
12
|
+
"summary": { "type": "string", "minLength": 1, "maxLength": 500 },
|
|
13
|
+
"outstandingWork": { "type": "string", "minLength": 1, "maxLength": 2000 },
|
|
14
|
+
"questions": {
|
|
15
|
+
"type": "array",
|
|
16
|
+
"minItems": 1,
|
|
17
|
+
"maxItems": 5,
|
|
18
|
+
"items": { "$ref": "#/definitions/BuilderInputQuestion" }
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"definitions": {
|
|
22
|
+
"BuilderInputQuestion": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"additionalProperties": false,
|
|
25
|
+
"required": ["id", "kind", "prompt", "whyNeeded", "effect", "evidence"],
|
|
26
|
+
"properties": {
|
|
27
|
+
"id": { "type": "string", "pattern": "^[a-z][a-z0-9-]{0,63}$" },
|
|
28
|
+
"kind": { "type": "string", "enum": ["secret", "text", "choice"] },
|
|
29
|
+
"format": { "type": "string", "enum": ["plain", "email", "url", "integer"] },
|
|
30
|
+
"prompt": { "type": "string", "minLength": 1, "maxLength": 300 },
|
|
31
|
+
"whyNeeded": { "type": "string", "minLength": 1, "maxLength": 500 },
|
|
32
|
+
"effect": { "type": "string", "minLength": 1, "maxLength": 500 },
|
|
33
|
+
"secretName": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,254}$" },
|
|
34
|
+
"options": {
|
|
35
|
+
"type": "array",
|
|
36
|
+
"minItems": 2,
|
|
37
|
+
"maxItems": 5,
|
|
38
|
+
"uniqueItems": true,
|
|
39
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 200 }
|
|
40
|
+
},
|
|
41
|
+
"evidence": {
|
|
42
|
+
"type": "array",
|
|
43
|
+
"minItems": 1,
|
|
44
|
+
"maxItems": 8,
|
|
45
|
+
"items": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"additionalProperties": false,
|
|
48
|
+
"required": ["path", "finding"],
|
|
49
|
+
"properties": {
|
|
50
|
+
"path": { "type": "string", "minLength": 1, "maxLength": 512 },
|
|
51
|
+
"line": { "type": "integer", "minimum": 1 },
|
|
52
|
+
"finding": { "type": "string", "minLength": 1, "maxLength": 500 }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"allOf": [
|
|
58
|
+
{
|
|
59
|
+
"if": { "properties": { "kind": { "const": "secret" } } },
|
|
60
|
+
"then": { "required": ["secretName"], "not": { "required": ["options"] } }
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"if": { "properties": { "kind": { "const": "choice" } } },
|
|
64
|
+
"then": { "required": ["options"], "not": { "required": ["secretName"] } }
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"if": { "properties": { "kind": { "const": "text" } } },
|
|
68
|
+
"then": { "not": { "anyOf": [{ "required": ["secretName"] }, { "required": ["options"] }] } }
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.harbour.dev/data-grant-v2.schema.json",
|
|
4
|
+
"title": "DataGrantV2",
|
|
5
|
+
"description": "An app's integration grant for one connection, environment and identity mode (control table sk APP#<appId>#IGRANT#<environment>#<sourceId>#<mode>; grantId = base64url(JSON [\"v2\",appId,environment,sourceId,mode])). Additive to the legacy DataGrant (data-grant.schema.json), which is never consulted by v2 paths. Expiry is enforced from UTC time on every call and SDK calls never extend it. Status edges live in x-transitions; DENIED and REVOKED are terminal (a regrant after revocation is a new REQUESTED record once cleanup is DONE). Database grants add credentialRef/mintedPrincipal, SaaS grants add installation; both hold references only.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["schema", "appId", "sourceId", "kind", "environment", "identityMode", "status", "operations", "resources", "version", "provisioning"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema": { "type": "string", "const": "harbour.data-grant/2.0" },
|
|
11
|
+
"appId": { "type": "string", "minLength": 1, "maxLength": 255 },
|
|
12
|
+
"sourceId": { "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationAlias" },
|
|
13
|
+
"kind": { "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationConnectionKind" },
|
|
14
|
+
"environment": { "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationEnvironment" },
|
|
15
|
+
"identityMode": { "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationIdentityMode" },
|
|
16
|
+
"status": { "$ref": "#/definitions/DataGrantV2Status" },
|
|
17
|
+
"operations": {
|
|
18
|
+
"type": "array",
|
|
19
|
+
"minItems": 1,
|
|
20
|
+
"maxItems": 3,
|
|
21
|
+
"uniqueItems": true,
|
|
22
|
+
"items": { "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationOperation" },
|
|
23
|
+
"description": "Approved operations; each must match this grant's identityMode."
|
|
24
|
+
},
|
|
25
|
+
"resources": {
|
|
26
|
+
"type": "array",
|
|
27
|
+
"minItems": 1,
|
|
28
|
+
"maxItems": 64,
|
|
29
|
+
"uniqueItems": true,
|
|
30
|
+
"items": { "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationAlias" },
|
|
31
|
+
"description": "Approved logical resource names (channel aliases or view names). Never inherited by wildcard."
|
|
32
|
+
},
|
|
33
|
+
"expiresAt": { "type": "string", "format": "date-time", "description": "Absent only for explicitly approved ongoing production access." },
|
|
34
|
+
"approvedBy": { "type": "string", "minLength": 1, "maxLength": 255 },
|
|
35
|
+
"version": { "type": "integer", "minimum": 1, "description": "Optimistic-concurrency version; approvals compare it with the reviewed value." },
|
|
36
|
+
"pendingRequestId": { "type": "string", "minLength": 1, "maxLength": 128, "description": "The one open access request attached to this grant, if any." },
|
|
37
|
+
"provisioning": { "$ref": "#/definitions/DataGrantV2Provisioning" },
|
|
38
|
+
"credentialRef": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"pattern": "^secretref://[A-Za-z0-9][A-Za-z0-9/_.:-]*$",
|
|
41
|
+
"description": "Database grants: secretref://tenant/{t}/app/{a}/env/{env}/source/{s}."
|
|
42
|
+
},
|
|
43
|
+
"mintedPrincipal": { "type": "string", "minLength": 1, "maxLength": 255, "description": "Database grants: the deterministic per tenant/app/environment/source principal." },
|
|
44
|
+
"installation": { "type": "string", "pattern": "^[a-z][a-z0-9_-]{0,62}$", "description": "SaaS grants: the connection installation this environment maps to." }
|
|
45
|
+
},
|
|
46
|
+
"definitions": {
|
|
47
|
+
"DataGrantV2Status": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"enum": ["REQUESTED", "GRANTED", "DENIED", "REVOKED"],
|
|
50
|
+
"description": "Grant lifecycle state. Legal edges live in x-transitions; DENIED and REVOKED are terminal."
|
|
51
|
+
},
|
|
52
|
+
"DataGrantV2ProvisioningStatus": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"enum": ["NONE", "PENDING", "FAILED", "DONE"],
|
|
55
|
+
"description": "Provider-side work for the current approved scope (credential mint, cleanup, installation check)."
|
|
56
|
+
},
|
|
57
|
+
"DataGrantV2Provisioning": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"required": ["status", "attempts"],
|
|
61
|
+
"properties": {
|
|
62
|
+
"status": { "$ref": "#/definitions/DataGrantV2ProvisioningStatus" },
|
|
63
|
+
"attempts": { "type": "integer", "minimum": 0, "maximum": 3, "description": "Failed attempts; three requires an admin retry." },
|
|
64
|
+
"lastError": { "type": "string", "maxLength": 500, "description": "Sanitized reason; never a provider error body." }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"x-status-definition": "DataGrantV2Status",
|
|
69
|
+
"x-terminal": ["DENIED", "REVOKED"],
|
|
70
|
+
"x-transitions": [
|
|
71
|
+
{ "from": "REQUESTED", "to": "GRANTED", "trigger": "approval published conditionally after provisioning verified (or auto-approved within development preapproval)" },
|
|
72
|
+
{ "from": "REQUESTED", "to": "DENIED", "trigger": "admin denies the request" },
|
|
73
|
+
{ "from": "GRANTED", "to": "REVOKED", "trigger": "admin revokes; Isomorph access is cut first, provider cleanup follows" }
|
|
74
|
+
],
|
|
75
|
+
"examples": [
|
|
76
|
+
{
|
|
77
|
+
"schema": "harbour.data-grant/2.0",
|
|
78
|
+
"appId": "app-1f2e3d4c",
|
|
79
|
+
"sourceId": "company-slack",
|
|
80
|
+
"kind": "saas",
|
|
81
|
+
"environment": "development",
|
|
82
|
+
"identityMode": "user",
|
|
83
|
+
"status": "GRANTED",
|
|
84
|
+
"operations": ["slack.channel.history"],
|
|
85
|
+
"resources": ["team-updates"],
|
|
86
|
+
"expiresAt": "2026-09-10T08:00:00Z",
|
|
87
|
+
"approvedBy": "policy:development-preapproval",
|
|
88
|
+
"version": 2,
|
|
89
|
+
"provisioning": { "status": "DONE", "attempts": 0 },
|
|
90
|
+
"installation": "nonprod"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"schema": "harbour.data-grant/2.0",
|
|
94
|
+
"appId": "app-1f2e3d4c",
|
|
95
|
+
"sourceId": "sales-warehouse",
|
|
96
|
+
"kind": "database",
|
|
97
|
+
"environment": "preview",
|
|
98
|
+
"identityMode": "app",
|
|
99
|
+
"status": "REQUESTED",
|
|
100
|
+
"operations": ["warehouse.view.read"],
|
|
101
|
+
"resources": ["weekly_sales"],
|
|
102
|
+
"expiresAt": "2026-09-16T08:00:00Z",
|
|
103
|
+
"version": 1,
|
|
104
|
+
"pendingRequestId": "req_9a8b7c6d",
|
|
105
|
+
"provisioning": { "status": "PENDING", "attempts": 1, "lastError": "credential verification timed out" }
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://schemas.harbour.dev/deployment-continuation.schema.json",
|
|
4
|
+
"title": "DeploymentContinuation",
|
|
5
|
+
"description": "Internal immutable reference admitting one child deployment from a parked sequential-preview input request. It contains references and accepted non-secret answers, never credential values.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["resumeFrom"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"resumeFrom": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"additionalProperties": false,
|
|
13
|
+
"required": ["parentVersionId", "requestId", "checkpointKey", "checkpointSha256", "answerRevision", "answers"],
|
|
14
|
+
"properties": {
|
|
15
|
+
"parentVersionId": { "type": "string", "pattern": "^[^#]+#[^#]+#preview#[0-9]{6,}$" },
|
|
16
|
+
"requestId": { "type": "string", "minLength": 1, "maxLength": 128 },
|
|
17
|
+
"checkpointKey": { "type": "string", "minLength": 1, "maxLength": 1024 },
|
|
18
|
+
"checkpointSha256": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
|
|
19
|
+
"answerRevision": { "type": "integer", "minimum": 1 },
|
|
20
|
+
"answers": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"minItems": 1,
|
|
23
|
+
"maxItems": 5,
|
|
24
|
+
"items": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"additionalProperties": false,
|
|
27
|
+
"required": ["questionId"],
|
|
28
|
+
"properties": {
|
|
29
|
+
"questionId": { "type": "string", "pattern": "^[a-z][a-z0-9-]{0,63}$" },
|
|
30
|
+
"value": { "type": "string", "maxLength": 2000 },
|
|
31
|
+
"secretRef": { "type": "string", "pattern": "^secretref://[A-Za-z0-9][A-Za-z0-9/_.:-]*$" }
|
|
32
|
+
},
|
|
33
|
+
"oneOf": [{ "required": ["value"] }, { "required": ["secretRef"] }]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"status": {
|
|
27
27
|
"type": "string",
|
|
28
|
-
"enum": ["QUEUED", "ANALYZING", "TRANSFORMING", "VALIDATING", "BUILDING", "VERIFYING", "GATING", "LIVE", "AWAITING-PROMOTION", "PROMOTING", "FAILED", "STALE"]
|
|
28
|
+
"enum": ["QUEUED", "ANALYZING", "TRANSFORMING", "WAITING_INPUT", "VALIDATING", "BUILDING", "VERIFYING", "GATING", "LIVE", "AWAITING-PROMOTION", "PROMOTING", "FAILED", "STALE"]
|
|
29
29
|
},
|
|
30
30
|
"message": {
|
|
31
31
|
"type": "string",
|
|
@@ -66,9 +66,58 @@
|
|
|
66
66
|
},
|
|
67
67
|
"deployTarget": {
|
|
68
68
|
"$ref": "#/definitions/StatusDeployTarget"
|
|
69
|
+
},
|
|
70
|
+
"pendingInput": {
|
|
71
|
+
"$ref": "#/definitions/StatusPendingInput"
|
|
69
72
|
}
|
|
70
73
|
},
|
|
71
74
|
"definitions": {
|
|
75
|
+
"StatusPendingInput": {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"additionalProperties": false,
|
|
78
|
+
"required": ["requestId", "revision", "questions", "waitMode", "createdAt"],
|
|
79
|
+
"properties": {
|
|
80
|
+
"requestId": { "type": "string", "minLength": 1, "maxLength": 128 },
|
|
81
|
+
"revision": { "type": "integer", "minimum": 0 },
|
|
82
|
+
"questions": {
|
|
83
|
+
"type": "array",
|
|
84
|
+
"minItems": 1,
|
|
85
|
+
"maxItems": 5,
|
|
86
|
+
"items": { "$ref": "#/definitions/StatusInputQuestion" }
|
|
87
|
+
},
|
|
88
|
+
"waitMode": { "type": "string", "enum": ["active", "parking", "parked"] },
|
|
89
|
+
"warmUntil": { "type": "string", "format": "date-time" },
|
|
90
|
+
"createdAt": { "type": "string", "format": "date-time" },
|
|
91
|
+
"continuationVersionId": { "type": "string", "pattern": "^[^#]+#[^#]+#preview#[0-9]{6,}$" },
|
|
92
|
+
"statusUrl": { "type": "string", "pattern": "^/v1/tenants/[^/]+/apps/[^/]+/deployments/[^/]+$" }
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"StatusInputQuestion": {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"additionalProperties": false,
|
|
98
|
+
"required": ["id", "kind", "prompt", "whyNeeded", "effect"],
|
|
99
|
+
"properties": {
|
|
100
|
+
"id": { "type": "string", "pattern": "^[a-z][a-z0-9-]{0,63}$" },
|
|
101
|
+
"kind": { "type": "string", "enum": ["secret", "text", "choice"] },
|
|
102
|
+
"format": { "type": "string", "enum": ["plain", "email", "url", "integer"] },
|
|
103
|
+
"prompt": { "type": "string", "minLength": 1, "maxLength": 300 },
|
|
104
|
+
"whyNeeded": { "type": "string", "minLength": 1, "maxLength": 500 },
|
|
105
|
+
"effect": { "type": "string", "minLength": 1, "maxLength": 500 },
|
|
106
|
+
"secretName": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,254}$" },
|
|
107
|
+
"options": {
|
|
108
|
+
"type": "array",
|
|
109
|
+
"minItems": 2,
|
|
110
|
+
"maxItems": 5,
|
|
111
|
+
"uniqueItems": true,
|
|
112
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 200 }
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"allOf": [
|
|
116
|
+
{ "if": { "properties": { "kind": { "const": "secret" } } }, "then": { "required": ["secretName"], "not": { "required": ["options"] } } },
|
|
117
|
+
{ "if": { "properties": { "kind": { "const": "choice" } } }, "then": { "required": ["options"], "not": { "required": ["secretName"] } } },
|
|
118
|
+
{ "if": { "properties": { "kind": { "const": "text" } } }, "then": { "not": { "anyOf": [{ "required": ["secretName"] }, { "required": ["options"] }] } } }
|
|
119
|
+
]
|
|
120
|
+
},
|
|
72
121
|
"StatusDeployTarget": {
|
|
73
122
|
"type": "object",
|
|
74
123
|
"additionalProperties": false,
|