@mdbase-dev/connect-protocol 0.1.0-beta.56 → 0.1.0-beta.58

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.
@@ -1,9 +1,9 @@
1
1
  import type { ApplicationFileRequirement } from "./files.js";
2
- import type { CollectionOperation } from "./operations.js";
3
- import type { ConnectContractRequirements } from "./compatibility.js";
2
+ import { type CollectionOperation } from "./operations.js";
3
+ import type { AuthorizationBindingProtocolVersion, ConnectContractRequirements } from "./compatibility.js";
4
4
  export type ApplicationAuthorizationFlow = "authorization_code" | "device_code";
5
5
  export interface ApplicationAuthorizationBinding {
6
- protocol_version: 4;
6
+ protocol_version: AuthorizationBindingProtocolVersion;
7
7
  authorization_id: string;
8
8
  application_id: string;
9
9
  application_declaration_id: string;
@@ -1,5 +1,8 @@
1
+ import { isMutatingOperation } from "./operations.js";
2
+ import { AUTHORIZATION_BINDING_PROTOCOL_VERSION, LEGACY_AUTHORIZATION_BINDING_PROTOCOL_VERSION, OPERATION_TRANSPORT_PROTOCOL_VERSION, isSupportedAuthorizationBinding, isSupportedOperationTransport } from "./compatibility.js";
1
3
  const INSTALLATION_ID_DOMAIN = new TextEncoder().encode("mdbase-connect application installation id v2\0");
2
- const AUTHORIZATION_PROOF_DOMAIN = new TextEncoder().encode("mdbase-connect application authorization proof v4\0");
4
+ const AUTHORIZATION_PROOF_V4_DOMAIN = new TextEncoder().encode("mdbase-connect application authorization proof v4\0");
5
+ const AUTHORIZATION_PROOF_V5_DOMAIN = new TextEncoder().encode("mdbase-connect application authorization proof v5\0");
3
6
  export async function applicationInstallationIdFromPublicKey(signingPublicKey) {
4
7
  const signing = publicKey(signingPublicKey);
5
8
  const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", concat([INSTALLATION_ID_DOMAIN, field(signing)])));
@@ -26,7 +29,10 @@ export function authorizationSigningMessage(binding) {
26
29
  const nonce = canonicalBase64(binding.authorization_nonce);
27
30
  const challenge = canonicalBase64(binding.code_challenge);
28
31
  const contracts = binding.contracts;
29
- if (binding.protocol_version !== 4
32
+ const recovery = contracts.operation_transport_recovery ?? [];
33
+ const requiresDurableMutation = binding.requested_operations.some((operation) => isMutatingOperation(operation, { action: "mutate" }))
34
+ || binding.requested_files?.actions.some((action) => !["list", "read"].includes(action)) === true;
35
+ if (!isSupportedAuthorizationBinding(binding.protocol_version)
30
36
  || nonce.byteLength !== 32
31
37
  || !/^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)+$/u.test(binding.application_declaration_id)
32
38
  || !/^[0-9a-f]{64}$/u.test(binding.application_manifest_digest)
@@ -39,6 +45,19 @@ export function authorizationSigningMessage(binding) {
39
45
  contracts.semantic_capabilities,
40
46
  contracts.durable_mutation ?? 1
41
47
  ].every((version) => Number.isInteger(version) && version > 0)
48
+ || contracts.authorization_binding !== binding.protocol_version
49
+ || !isSupportedOperationTransport(contracts.operation_transport)
50
+ || (binding.protocol_version === AUTHORIZATION_BINDING_PROTOCOL_VERSION
51
+ && contracts.operation_transport !== OPERATION_TRANSPORT_PROTOCOL_VERSION)
52
+ || recovery.some((version) => !isSupportedOperationTransport(version)
53
+ || version === contracts.operation_transport)
54
+ || new Set(recovery).size !== recovery.length
55
+ || (binding.protocol_version === LEGACY_AUTHORIZATION_BINDING_PROTOCOL_VERSION
56
+ && recovery.length > 0)
57
+ || (recovery.length > 0 && !requiresDurableMutation)
58
+ || (requiresDurableMutation
59
+ ? contracts.durable_mutation !== 1
60
+ : contracts.durable_mutation !== undefined)
42
61
  || (binding.requested_operations.length === 0 && binding.requested_files === undefined)
43
62
  || new Set(binding.requested_operations).size !== binding.requested_operations.length
44
63
  || binding.requested_operations.some((operation) => !operation || operation.includes("\0"))
@@ -52,7 +71,9 @@ export function authorizationSigningMessage(binding) {
52
71
  validateRequestedFiles(binding.requested_files);
53
72
  const operationFields = binding.requested_operations.map((operation) => field(new TextEncoder().encode(operation)));
54
73
  return concat([
55
- AUTHORIZATION_PROOF_DOMAIN,
74
+ binding.protocol_version === AUTHORIZATION_BINDING_PROTOCOL_VERSION
75
+ ? AUTHORIZATION_PROOF_V5_DOMAIN
76
+ : AUTHORIZATION_PROOF_V4_DOMAIN,
56
77
  u32(binding.protocol_version),
57
78
  field(uuidBytes(binding.application_id)),
58
79
  field(uuidBytes(binding.authorization_id)),
@@ -68,6 +89,9 @@ export function authorizationSigningMessage(binding) {
68
89
  optionalString(binding.state),
69
90
  field(new TextEncoder().encode(binding.code_challenge)),
70
91
  u32(contracts.operation_transport),
92
+ ...(binding.protocol_version === AUTHORIZATION_BINDING_PROTOCOL_VERSION
93
+ ? [u32(recovery.length), ...recovery.map(u32)]
94
+ : []),
71
95
  u32(contracts.authorization_binding),
72
96
  u32(contracts.semantic_capabilities),
73
97
  optionalU32(contracts.durable_mutation),
@@ -1,11 +1,19 @@
1
1
  import type { ApplicationFileRequirement } from "./files.js";
2
2
  import { type CollectionOperation } from "./operations.js";
3
3
  export declare const OPERATION_TRANSPORT_PROTOCOL_VERSION: 3;
4
- export declare const AUTHORIZATION_BINDING_PROTOCOL_VERSION: 4;
4
+ export declare const LEGACY_OPERATION_TRANSPORT_PROTOCOL_VERSION: 2;
5
+ export declare const SUPPORTED_OPERATION_TRANSPORT_PROTOCOL_VERSIONS: readonly [3, 2];
6
+ export type OperationTransportProtocolVersion = typeof SUPPORTED_OPERATION_TRANSPORT_PROTOCOL_VERSIONS[number];
7
+ export declare const AUTHORIZATION_BINDING_PROTOCOL_VERSION: 5;
8
+ export declare const LEGACY_AUTHORIZATION_BINDING_PROTOCOL_VERSION: 4;
9
+ export declare const SUPPORTED_AUTHORIZATION_BINDING_PROTOCOL_VERSIONS: readonly [5, 4];
10
+ export type AuthorizationBindingProtocolVersion = typeof SUPPORTED_AUTHORIZATION_BINDING_PROTOCOL_VERSIONS[number];
5
11
  export declare const SEMANTIC_CAPABILITY_CONTRACT_VERSION: 1;
6
12
  export declare const DURABLE_MUTATION_CONTRACT_VERSION: 1;
7
13
  export interface ConnectContractRequirements {
8
14
  operation_transport: number;
15
+ /** Signed, mutation-only transports retained solely for exact outcome recovery. */
16
+ operation_transport_recovery?: number[];
9
17
  authorization_binding: number;
10
18
  semantic_capabilities: number;
11
19
  /** Required only by operations that can durably mutate authority state. */
@@ -22,5 +30,8 @@ export declare const CONNECT_CONTRACT_SUPPORT: ConnectContractSupport;
22
30
  * Produce the exact contract ceiling signed during authorization. `sync` is
23
31
  * conservatively mutation-capable because its action is selected per request.
24
32
  */
25
- export declare function authorizationContractRequirements(operations: readonly CollectionOperation[], files?: ApplicationFileRequirement): ConnectContractRequirements;
33
+ export declare function authorizationContractRequirements(operations: readonly CollectionOperation[], files?: ApplicationFileRequirement, operationTransportRecovery?: readonly OperationTransportProtocolVersion[]): ConnectContractRequirements;
26
34
  export declare function supportsContractRequirements(required: ConnectContractRequirements, supported: ConnectContractSupport, durableMutation: boolean): boolean;
35
+ export declare function isSupportedOperationTransport(version: number): version is OperationTransportProtocolVersion;
36
+ export declare function isSupportedAuthorizationBinding(version: number): version is AuthorizationBindingProtocolVersion;
37
+ export declare function permitsOperationTransport(contracts: ConnectContractRequirements, version: number, recoveryOnly?: boolean): boolean;
@@ -1,11 +1,21 @@
1
1
  import { isMutatingOperation } from "./operations.js";
2
2
  export const OPERATION_TRANSPORT_PROTOCOL_VERSION = 3;
3
- export const AUTHORIZATION_BINDING_PROTOCOL_VERSION = 4;
3
+ export const LEGACY_OPERATION_TRANSPORT_PROTOCOL_VERSION = 2;
4
+ export const SUPPORTED_OPERATION_TRANSPORT_PROTOCOL_VERSIONS = [
5
+ OPERATION_TRANSPORT_PROTOCOL_VERSION,
6
+ LEGACY_OPERATION_TRANSPORT_PROTOCOL_VERSION
7
+ ];
8
+ export const AUTHORIZATION_BINDING_PROTOCOL_VERSION = 5;
9
+ export const LEGACY_AUTHORIZATION_BINDING_PROTOCOL_VERSION = 4;
10
+ export const SUPPORTED_AUTHORIZATION_BINDING_PROTOCOL_VERSIONS = [
11
+ AUTHORIZATION_BINDING_PROTOCOL_VERSION,
12
+ LEGACY_AUTHORIZATION_BINDING_PROTOCOL_VERSION
13
+ ];
4
14
  export const SEMANTIC_CAPABILITY_CONTRACT_VERSION = 1;
5
15
  export const DURABLE_MUTATION_CONTRACT_VERSION = 1;
6
16
  export const CONNECT_CONTRACT_SUPPORT = {
7
- operation_transport: [OPERATION_TRANSPORT_PROTOCOL_VERSION],
8
- authorization_binding: [AUTHORIZATION_BINDING_PROTOCOL_VERSION],
17
+ operation_transport: [...SUPPORTED_OPERATION_TRANSPORT_PROTOCOL_VERSIONS],
18
+ authorization_binding: [...SUPPORTED_AUTHORIZATION_BINDING_PROTOCOL_VERSIONS],
9
19
  semantic_capabilities: [SEMANTIC_CAPABILITY_CONTRACT_VERSION],
10
20
  durable_mutation: [DURABLE_MUTATION_CONTRACT_VERSION]
11
21
  };
@@ -13,12 +23,17 @@ export const CONNECT_CONTRACT_SUPPORT = {
13
23
  * Produce the exact contract ceiling signed during authorization. `sync` is
14
24
  * conservatively mutation-capable because its action is selected per request.
15
25
  */
16
- export function authorizationContractRequirements(operations, files) {
26
+ export function authorizationContractRequirements(operations, files, operationTransportRecovery = []) {
17
27
  const requirements = {
18
28
  operation_transport: OPERATION_TRANSPORT_PROTOCOL_VERSION,
19
29
  authorization_binding: AUTHORIZATION_BINDING_PROTOCOL_VERSION,
20
30
  semantic_capabilities: 1
21
31
  };
32
+ const recovery = [...new Set(operationTransportRecovery)]
33
+ .filter((version) => version !== OPERATION_TRANSPORT_PROTOCOL_VERSION)
34
+ .sort((left, right) => right - left);
35
+ if (recovery.length > 0)
36
+ requirements.operation_transport_recovery = recovery;
22
37
  if (operations.some((operation) => isMutatingOperation(operation, { action: "mutate" }))
23
38
  || files?.actions.some((action) => action !== "list" && action !== "read") === true) {
24
39
  requirements.durable_mutation = 1;
@@ -27,9 +42,23 @@ export function authorizationContractRequirements(operations, files) {
27
42
  }
28
43
  export function supportsContractRequirements(required, supported, durableMutation) {
29
44
  return supported.operation_transport.includes(required.operation_transport)
45
+ && (required.operation_transport_recovery ?? []).every((version) => supported.operation_transport.includes(version))
30
46
  && supported.authorization_binding.includes(required.authorization_binding)
31
47
  && supported.semantic_capabilities.includes(required.semantic_capabilities)
32
48
  && (!durableMutation
33
49
  || (required.durable_mutation !== undefined
34
50
  && supported.durable_mutation.includes(required.durable_mutation)));
35
51
  }
52
+ export function isSupportedOperationTransport(version) {
53
+ return SUPPORTED_OPERATION_TRANSPORT_PROTOCOL_VERSIONS
54
+ .includes(version);
55
+ }
56
+ export function isSupportedAuthorizationBinding(version) {
57
+ return SUPPORTED_AUTHORIZATION_BINDING_PROTOCOL_VERSIONS
58
+ .includes(version);
59
+ }
60
+ export function permitsOperationTransport(contracts, version, recoveryOnly = false) {
61
+ return contracts.operation_transport === version
62
+ || (recoveryOnly
63
+ && (contracts.operation_transport_recovery ?? []).includes(version));
64
+ }
@@ -156,6 +156,10 @@ export declare const CONNECT_PROBLEM_CATALOG: {
156
156
  readonly category: "conflict";
157
157
  readonly recovery: "refresh";
158
158
  };
159
+ readonly file_index_warming: {
160
+ readonly category: "availability";
161
+ readonly recovery: "retry";
162
+ };
159
163
  readonly file_move_failed: {
160
164
  readonly category: "internal";
161
165
  readonly recovery: "contact_support";
@@ -525,6 +529,7 @@ export interface ConnectProblemDetailsByCode {
525
529
  "expired_token": undefined;
526
530
  "file_changed_during_move": undefined;
527
531
  "file_changed_during_read": undefined;
532
+ "file_index_warming": undefined;
528
533
  "file_move_failed": undefined;
529
534
  "file_mutation_conflict": undefined;
530
535
  "file_not_found": undefined;
@@ -856,6 +861,12 @@ export interface ConnectProblemByCode {
856
861
  recovery: "refresh";
857
862
  details?: never;
858
863
  };
864
+ "file_index_warming": ConnectProblemBase & {
865
+ code: "file_index_warming";
866
+ category: "availability";
867
+ recovery: "retry";
868
+ details?: never;
869
+ };
859
870
  "file_move_failed": ConnectProblemBase & {
860
871
  code: "file_move_failed";
861
872
  category: "internal";
@@ -40,6 +40,7 @@ export const CONNECT_PROBLEM_CATALOG = {
40
40
  "expired_token": { category: "authorization", recovery: "reauthorize" },
41
41
  "file_changed_during_move": { category: "conflict", recovery: "refresh" },
42
42
  "file_changed_during_read": { category: "conflict", recovery: "refresh" },
43
+ "file_index_warming": { category: "availability", recovery: "retry" },
43
44
  "file_move_failed": { category: "internal", recovery: "contact_support" },
44
45
  "file_mutation_conflict": { category: "conflict", recovery: "resolve_conflict" },
45
46
  "file_not_found": { category: "conflict", recovery: "refresh" },
package/dist/index.d.ts CHANGED
@@ -23,11 +23,12 @@ export declare const GRANT_ENCRYPTION_PROTOCOL_VERSION: 1;
23
23
  export declare const SYNC_PROTOCOL_VERSION: 1;
24
24
  export declare const CONTRACT_SETUP_CAPABILITY: "contract-setup-v1";
25
25
  export declare const FILE_RELAY_CAPABILITY: "file-relay-v1";
26
+ export declare const PROTOCOL_USAGE_REPORT_CAPABILITY: "protocol-usage-report-v1";
26
27
  export declare const RELAY_REQUIRED_CAPABILITIES: readonly ["application-authorization-v4", "authorization-activation", "encrypted-relay", "policy-ack"];
27
28
  export declare const MINIMUM_CONNECTOR_VERSION: "0.1.0-beta.33";
28
29
  export declare const HOSTED_PROVIDER_REQUIRED_CAPABILITIES: readonly ["durable-mutation-journal-v1", "durable-file-lifecycle-v1"];
29
30
  export declare const HOSTED_PROVIDER_CAPABILITIES: readonly ["durable-mutation-journal-v1", "durable-file-lifecycle-v1", "mutation-replay-after-credential-retirement-v1"];
30
- export declare const RELAY_CAPABILITIES: readonly ["application-authorization-v4", "authorization-activation", "encrypted-relay", "policy-ack", "contract-setup-v1", "file-relay-v1"];
31
+ export declare const RELAY_CAPABILITIES: readonly ["application-authorization-v4", "authorization-activation", "encrypted-relay", "policy-ack", "application-authorization-v5", "contract-setup-v1", "file-relay-v1", "protocol-usage-report-v1"];
31
32
  export declare const AUTHORITY_PROOF_VERSION: 1;
32
33
  export declare const AUTHORITY_PROOF_ALGORITHM: "P256-SHA256";
33
34
  export declare const AUTHORITY_PROOF_DOMAIN: "mdbase-authority-request-proof-v1";
@@ -182,7 +183,7 @@ export interface GrantScope {
182
183
  }
183
184
  export interface RelayOperationRequest {
184
185
  type: "operation_request";
185
- protocol_version: 3;
186
+ protocol_version: import("./compatibility.js").OperationTransportProtocolVersion;
186
187
  request_id: string;
187
188
  grant_id: string;
188
189
  collection_id: string;
@@ -192,29 +193,29 @@ export interface RelayOperationRequest {
192
193
  }
193
194
  export type RelayOperationResponse = {
194
195
  type: "operation_response";
195
- protocol_version: 3;
196
+ protocol_version: import("./compatibility.js").OperationTransportProtocolVersion;
196
197
  request_id: string;
197
198
  ok: true;
198
199
  result: unknown;
199
200
  } | {
200
201
  type: "operation_response";
201
- protocol_version: 3;
202
+ protocol_version: import("./compatibility.js").OperationTransportProtocolVersion;
202
203
  request_id: string;
203
204
  ok: false;
204
205
  problem: ConnectProblem;
205
206
  };
206
207
  export interface MdbaseOperationRequest<Input = unknown> {
207
- protocol_version: 3;
208
+ protocol_version: import("./compatibility.js").OperationTransportProtocolVersion;
208
209
  request_id: string;
209
210
  input: Input;
210
211
  }
211
212
  export type MdbaseOperationResponse<Result = unknown> = {
212
- protocol_version: 3;
213
+ protocol_version: import("./compatibility.js").OperationTransportProtocolVersion;
213
214
  request_id: string;
214
215
  ok: true;
215
216
  result: Result;
216
217
  } | {
217
- protocol_version: 3;
218
+ protocol_version: import("./compatibility.js").OperationTransportProtocolVersion;
218
219
  request_id: string;
219
220
  ok: false;
220
221
  problem: ConnectProblem;
@@ -261,7 +262,7 @@ export interface GrantEncryption {
261
262
  connector_agreement_public_key: string;
262
263
  }
263
264
  export interface EncryptedRelayEnvelope {
264
- protocol_version: 3;
265
+ protocol_version: import("./compatibility.js").OperationTransportProtocolVersion;
265
266
  suite: typeof RELAY_ENCRYPTION_SUITE;
266
267
  request_id: string;
267
268
  grant_id: string;
@@ -549,6 +550,15 @@ export interface RelayPolicyApplied {
549
550
  details?: unknown;
550
551
  };
551
552
  }
553
+ export interface ProtocolUsageReport {
554
+ type: "protocol_usage_report";
555
+ protocol_version: 1;
556
+ entries: Array<{
557
+ axis: "operation_transport";
558
+ version: number;
559
+ count: number;
560
+ }>;
561
+ }
552
562
  export interface AuthorizationCollectionOffer {
553
563
  collection_id: string;
554
564
  display_name: string;
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ export const GRANT_ENCRYPTION_PROTOCOL_VERSION = 1;
16
16
  export const SYNC_PROTOCOL_VERSION = 1;
17
17
  export const CONTRACT_SETUP_CAPABILITY = "contract-setup-v1";
18
18
  export const FILE_RELAY_CAPABILITY = "file-relay-v1";
19
+ export const PROTOCOL_USAGE_REPORT_CAPABILITY = "protocol-usage-report-v1";
19
20
  export const RELAY_REQUIRED_CAPABILITIES = [
20
21
  "application-authorization-v4",
21
22
  "authorization-activation",
@@ -33,8 +34,10 @@ export const HOSTED_PROVIDER_CAPABILITIES = [
33
34
  ];
34
35
  export const RELAY_CAPABILITIES = [
35
36
  ...RELAY_REQUIRED_CAPABILITIES,
37
+ "application-authorization-v5",
36
38
  CONTRACT_SETUP_CAPABILITY,
37
- FILE_RELAY_CAPABILITY
39
+ FILE_RELAY_CAPABILITY,
40
+ PROTOCOL_USAGE_REPORT_CAPABILITY
38
41
  ];
39
42
  export const AUTHORITY_PROOF_VERSION = 1;
40
43
  export const AUTHORITY_PROOF_ALGORITHM = "P256-SHA256";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mdbase-dev/connect-protocol",
3
- "version": "0.1.0-beta.56",
3
+ "version": "0.1.0-beta.58",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -9,6 +9,7 @@
9
9
  "connect_problem_catalog": "./connect-problems.v1.catalog.json",
10
10
  "mdbase_query_v0_3": "./mdbase-query.v0.3.schema.json",
11
11
  "encrypted_relay": "./encrypted-relay.v3.schema.json",
12
+ "encrypted_relay_legacy_v2": "./encrypted-relay.v2.schema.json",
12
13
  "files": "./files.v1.schema.json",
13
14
  "notification_webhook": "./notification-webhook.v1.schema.json",
14
15
  "sync": "./sync.v1.schema.json"
@@ -1808,6 +1808,46 @@
1808
1808
  }
1809
1809
  }
1810
1810
  },
1811
+ {
1812
+ "type": "object",
1813
+ "additionalProperties": false,
1814
+ "required": [
1815
+ "problem_version",
1816
+ "code",
1817
+ "category",
1818
+ "recovery",
1819
+ "message"
1820
+ ],
1821
+ "properties": {
1822
+ "problem_version": {
1823
+ "const": 1
1824
+ },
1825
+ "code": {
1826
+ "const": "file_index_warming"
1827
+ },
1828
+ "category": {
1829
+ "const": "availability"
1830
+ },
1831
+ "recovery": {
1832
+ "const": "retry"
1833
+ },
1834
+ "message": {
1835
+ "type": "string",
1836
+ "minLength": 1
1837
+ },
1838
+ "operation_outcome": {
1839
+ "enum": [
1840
+ "not_sent",
1841
+ "rejected",
1842
+ "unknown"
1843
+ ]
1844
+ },
1845
+ "trace_id": {
1846
+ "type": "string",
1847
+ "minLength": 1
1848
+ }
1849
+ }
1850
+ },
1811
1851
  {
1812
1852
  "type": "object",
1813
1853
  "additionalProperties": false,
@@ -191,6 +191,7 @@
191
191
  "expired_token": { "category": "authorization", "recovery": "reauthorize" },
192
192
  "file_changed_during_move": { "category": "conflict", "recovery": "refresh" },
193
193
  "file_changed_during_read": { "category": "conflict", "recovery": "refresh" },
194
+ "file_index_warming": { "category": "availability", "recovery": "retry" },
194
195
  "file_move_failed": { "category": "internal", "recovery": "contact_support" },
195
196
  "file_mutation_conflict": { "category": "conflict", "recovery": "resolve_conflict" },
196
197
  "file_not_found": { "category": "conflict", "recovery": "refresh" },
@@ -0,0 +1,63 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://mdbase.dev/connect/schemas/encrypted-relay.v2.json",
4
+ "title": "mdbase connect encrypted operation transport 2",
5
+ "description": "Opaque authenticated operation envelopes routed between an application and authority.",
6
+ "oneOf": [
7
+ { "$ref": "#/$defs/request" },
8
+ { "$ref": "#/$defs/response" },
9
+ { "$ref": "#/$defs/rejected" }
10
+ ],
11
+ "$defs": {
12
+ "uuid": {
13
+ "type": "string",
14
+ "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
15
+ },
16
+ "operation": {
17
+ "enum": ["describe", "changes", "read", "query", "list_views", "execute_view", "read_view_source", "create_view_source", "update_view_source", "delete_view_source", "validate", "create", "update", "delete", "rename", "read_type", "create_type", "update_type", "assess_type_pack", "apply_type_pack", "assess_collection_setup", "apply_collection_setup", "list_timers", "put_timer", "cancel_timer", "reconcile_timers", "sync", "file_control"]
18
+ },
19
+ "envelope": {
20
+ "type": "object",
21
+ "additionalProperties": false,
22
+ "required": ["type", "protocol_version", "suite", "request_id", "grant_id", "application_id", "connector_id", "collection_id", "operation", "scope_epoch", "key_id", "counter", "ciphertext"],
23
+ "properties": {
24
+ "type": { "enum": ["encrypted_operation_request", "encrypted_operation_response"] },
25
+ "protocol_version": { "const": 2 },
26
+ "suite": { "const": "P256-HKDF-SHA256-AES256GCM" },
27
+ "request_id": { "$ref": "#/$defs/uuid" },
28
+ "grant_id": { "$ref": "#/$defs/uuid" },
29
+ "application_id": { "$ref": "#/$defs/uuid" },
30
+ "connector_id": { "$ref": "#/$defs/uuid" },
31
+ "collection_id": { "$ref": "#/$defs/uuid" },
32
+ "operation": { "$ref": "#/$defs/operation" },
33
+ "scope_epoch": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 },
34
+ "key_id": { "type": "string", "minLength": 1, "maxLength": 200 },
35
+ "counter": { "type": "string", "pattern": "^[1-9][0-9]{0,19}$" },
36
+ "ciphertext": { "type": "string", "minLength": 1, "maxLength": 2800000, "pattern": "^[A-Za-z0-9_-]+$" }
37
+ }
38
+ },
39
+ "request": {
40
+ "allOf": [
41
+ { "$ref": "#/$defs/envelope" },
42
+ { "type": "object", "properties": { "type": { "const": "encrypted_operation_request" } } }
43
+ ]
44
+ },
45
+ "response": {
46
+ "allOf": [
47
+ { "$ref": "#/$defs/envelope" },
48
+ { "type": "object", "properties": { "type": { "const": "encrypted_operation_response" } } }
49
+ ]
50
+ },
51
+ "rejected": {
52
+ "type": "object",
53
+ "additionalProperties": false,
54
+ "required": ["type", "protocol_version", "request_id", "problem"],
55
+ "properties": {
56
+ "type": { "const": "encrypted_operation_rejected" },
57
+ "protocol_version": { "const": 2 },
58
+ "request_id": { "$ref": "#/$defs/uuid" },
59
+ "problem": { "$ref": "https://schemas.mdbase.dev/connect/problem/v1" }
60
+ }
61
+ }
62
+ }
63
+ }