@treeseed/sdk 0.13.0-rc.53 → 0.13.0-rc.55
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/dist/agent-capacity/authority/agent-authority-presets.js +2 -1
- package/dist/agent-capacity/contracts/configuration/configuration.js +1 -1
- package/dist/agent-capacity/validation/agent-definition-schema.d.ts +240 -240
- package/dist/agent-capacity/validation/agent-lab-forensics-schema.d.ts +1 -1
- package/dist/capacity-provider/client.d.ts +18 -3
- package/dist/capacity-provider/client.js +6 -0
- package/dist/capacity-provider/contracts/governance.d.ts +28 -1
- package/dist/capacity-provider/index.d.ts +1 -0
- package/dist/capacity-provider/index.js +1 -0
- package/dist/capacity-provider/sandbox-contracts.d.ts +422 -0
- package/dist/capacity-provider/sandbox-contracts.js +71 -0
- package/dist/capacity-provider/validation.d.ts +2 -1
- package/dist/capacity-provider/validation.js +23 -0
- package/dist/content/validation/agent-operational-content-schemas.d.ts +76 -76
- package/dist/content/validation/auxiliary-content-schemas.d.ts +20 -20
- package/dist/content/validation/content-model-schemas.d.ts +570 -570
- package/dist/content/validation/portable-content-data.d.ts +61 -61
- package/dist/deployment/ai-mode.d.ts +4 -4
- package/dist/deployment/schemas.d.ts +418 -100
- package/dist/deployment/schemas.js +23 -1
- package/dist/development/schemas.d.ts +22 -22
- package/dist/operator-contracts/canonical-command-tree.js +63 -3
- package/dist/operator-contracts/catalog/communication-operations.d.ts +300 -33
- package/dist/operator-contracts/catalog/communication-operations.js +56 -2
- package/dist/operator-contracts/catalog/inbox-operations.d.ts +261 -0
- package/dist/operator-contracts/catalog/inbox-operations.js +36 -0
- package/dist/operator-contracts/communication/contracts.d.ts +1261 -71
- package/dist/operator-contracts/communication/contracts.js +99 -7
- package/dist/operator-contracts/control-plane-operations.d.ts +592 -66
- package/dist/operator-contracts/control-plane-operations.js +4 -1
- package/dist/operator-contracts/inbox/contracts.d.ts +1099 -0
- package/dist/operator-contracts/inbox/contracts.js +134 -0
- package/dist/operator-contracts/index.d.ts +1 -0
- package/dist/operator-contracts/index.js +1 -0
- package/dist/operator-contracts/mcp.js +1 -1
- package/dist/security/encrypted-envelope.d.ts +177 -0
- package/dist/security/encrypted-envelope.js +34 -0
- package/dist/security/envelope-codec.d.ts +38 -0
- package/dist/security/envelope-codec.js +96 -0
- package/dist/security/index.d.ts +2 -0
- package/dist/security/index.js +2 -0
- package/package.json +5 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const inboxItemKindSchema = z.enum(["proposal", "question"]);
|
|
3
|
+
const inboxItemStatusSchema = z.enum(["outstanding", "answered", "approved", "rejected", "closed", "deleted"]);
|
|
4
|
+
const inboxActionKindSchema = z.enum(["publish_revision", "approve", "reject", "comment", "answer", "reply", "reopen"]);
|
|
5
|
+
const inboxProvenanceSchema = z.object({
|
|
6
|
+
repositoryId: z.string().nullable(),
|
|
7
|
+
contentPath: z.string().nullable(),
|
|
8
|
+
commitSha: z.string().nullable(),
|
|
9
|
+
digest: z.string().nullable()
|
|
10
|
+
}).strict();
|
|
11
|
+
const inboxActionDescriptorSchema = z.object({
|
|
12
|
+
action: inboxActionKindSchema,
|
|
13
|
+
label: z.string(),
|
|
14
|
+
enabled: z.boolean(),
|
|
15
|
+
reason: z.string().nullable(),
|
|
16
|
+
requiresFeedback: z.boolean(),
|
|
17
|
+
confirmation: z.enum(["never", "input_required"])
|
|
18
|
+
}).strict();
|
|
19
|
+
const inboxCommentSchema = z.object({
|
|
20
|
+
id: z.string(),
|
|
21
|
+
parentId: z.string().nullable(),
|
|
22
|
+
kind: z.enum(["comment", "answer", "reply"]),
|
|
23
|
+
authorId: z.string(),
|
|
24
|
+
authorLabel: z.string(),
|
|
25
|
+
markdown: z.string(),
|
|
26
|
+
createdAt: z.string().datetime(),
|
|
27
|
+
provenance: inboxProvenanceSchema
|
|
28
|
+
}).strict();
|
|
29
|
+
const inboxItemSchema = z.object({
|
|
30
|
+
schemaVersion: z.literal("treeseed.inbox-item/v1"),
|
|
31
|
+
id: z.string(),
|
|
32
|
+
teamId: z.string(),
|
|
33
|
+
projectId: z.string(),
|
|
34
|
+
projectSlug: z.string(),
|
|
35
|
+
kind: inboxItemKindSchema,
|
|
36
|
+
title: z.string(),
|
|
37
|
+
markdown: z.string(),
|
|
38
|
+
status: inboxItemStatusSchema,
|
|
39
|
+
authorId: z.string(),
|
|
40
|
+
authorLabel: z.string(),
|
|
41
|
+
version: z.number().int().positive(),
|
|
42
|
+
discussionId: z.string().nullable(),
|
|
43
|
+
createdAt: z.string().datetime(),
|
|
44
|
+
updatedAt: z.string().datetime(),
|
|
45
|
+
provenance: inboxProvenanceSchema,
|
|
46
|
+
availableActions: z.array(inboxActionDescriptorSchema),
|
|
47
|
+
comments: z.array(inboxCommentSchema),
|
|
48
|
+
etag: z.string()
|
|
49
|
+
}).strict();
|
|
50
|
+
const inboxItemSummarySchema = inboxItemSchema.pick({
|
|
51
|
+
id: true,
|
|
52
|
+
teamId: true,
|
|
53
|
+
projectId: true,
|
|
54
|
+
projectSlug: true,
|
|
55
|
+
kind: true,
|
|
56
|
+
title: true,
|
|
57
|
+
status: true,
|
|
58
|
+
authorId: true,
|
|
59
|
+
authorLabel: true,
|
|
60
|
+
version: true,
|
|
61
|
+
createdAt: true,
|
|
62
|
+
updatedAt: true,
|
|
63
|
+
etag: true
|
|
64
|
+
});
|
|
65
|
+
const inboxItemPageSchema = z.object({
|
|
66
|
+
schemaVersion: z.literal("treeseed.inbox-page/v1"),
|
|
67
|
+
items: z.array(inboxItemSummarySchema),
|
|
68
|
+
cursor: z.string().nullable()
|
|
69
|
+
}).strict();
|
|
70
|
+
const inboxEventSchema = z.object({
|
|
71
|
+
schemaVersion: z.literal("treeseed.inbox-event/v1"),
|
|
72
|
+
sequence: z.string(),
|
|
73
|
+
id: z.string(),
|
|
74
|
+
teamId: z.string(),
|
|
75
|
+
itemId: z.string().nullable(),
|
|
76
|
+
type: z.enum(["item.created", "item.updated", "item.deleted", "draft.updated", "comment.created", "question.answered", "question.reopened", "proposal.revised", "proposal.decided"]),
|
|
77
|
+
actorId: z.string(),
|
|
78
|
+
occurredAt: z.string().datetime(),
|
|
79
|
+
summary: z.string(),
|
|
80
|
+
payload: z.record(z.unknown())
|
|
81
|
+
}).strict();
|
|
82
|
+
const inboxTimelineSchema = z.object({ events: z.array(inboxEventSchema), cursor: z.string() }).strict();
|
|
83
|
+
const inboxDraftPurposeSchema = z.enum(["feedback", "proposal_revision"]);
|
|
84
|
+
const inboxDraftSchema = z.object({
|
|
85
|
+
schemaVersion: z.literal("treeseed.inbox-draft/v1"),
|
|
86
|
+
itemId: z.string(),
|
|
87
|
+
purpose: inboxDraftPurposeSchema,
|
|
88
|
+
markdown: z.string(),
|
|
89
|
+
baseVersion: z.number().int().positive(),
|
|
90
|
+
revision: z.number().int().nonnegative(),
|
|
91
|
+
updatedAt: z.string().datetime(),
|
|
92
|
+
etag: z.string()
|
|
93
|
+
}).strict();
|
|
94
|
+
const inboxDraftWriteSchema = z.object({ markdown: z.string().max(1e5), baseVersion: z.number().int().positive() }).strict();
|
|
95
|
+
const inboxQuestionCreateSchema = z.object({
|
|
96
|
+
projectId: z.string(),
|
|
97
|
+
title: z.string().trim().min(1).max(180),
|
|
98
|
+
markdown: z.string().trim().min(1).max(1e5),
|
|
99
|
+
recipients: z.array(z.string()).max(64).default([]),
|
|
100
|
+
relatedObjectives: z.array(z.string()).max(64).default([])
|
|
101
|
+
}).strict();
|
|
102
|
+
const inboxActionRequestSchema = z.object({
|
|
103
|
+
action: inboxActionKindSchema,
|
|
104
|
+
markdown: z.string().max(1e5).optional(),
|
|
105
|
+
parentId: z.string().optional(),
|
|
106
|
+
changeReason: z.string().max(2e3).optional(),
|
|
107
|
+
draftRevision: z.number().int().nonnegative().optional()
|
|
108
|
+
}).strict();
|
|
109
|
+
const inboxMutationReceiptSchema = z.object({
|
|
110
|
+
schemaVersion: z.literal("treeseed.inbox-mutation-receipt/v1"),
|
|
111
|
+
item: inboxItemSchema,
|
|
112
|
+
action: inboxActionKindSchema.optional(),
|
|
113
|
+
replayed: z.boolean(),
|
|
114
|
+
receipts: z.array(z.record(z.unknown())).default([])
|
|
115
|
+
}).strict();
|
|
116
|
+
export {
|
|
117
|
+
inboxActionDescriptorSchema,
|
|
118
|
+
inboxActionKindSchema,
|
|
119
|
+
inboxActionRequestSchema,
|
|
120
|
+
inboxCommentSchema,
|
|
121
|
+
inboxDraftPurposeSchema,
|
|
122
|
+
inboxDraftSchema,
|
|
123
|
+
inboxDraftWriteSchema,
|
|
124
|
+
inboxEventSchema,
|
|
125
|
+
inboxItemKindSchema,
|
|
126
|
+
inboxItemPageSchema,
|
|
127
|
+
inboxItemSchema,
|
|
128
|
+
inboxItemStatusSchema,
|
|
129
|
+
inboxItemSummarySchema,
|
|
130
|
+
inboxMutationReceiptSchema,
|
|
131
|
+
inboxProvenanceSchema,
|
|
132
|
+
inboxQuestionCreateSchema,
|
|
133
|
+
inboxTimelineSchema
|
|
134
|
+
};
|
|
@@ -10,7 +10,7 @@ const TREESEED_MCP_PROMPTS = [
|
|
|
10
10
|
{ name: "research", description: "Research a governed TreeSeed question and accumulate knowledge.", argumentSchemaId: "treeseed.prompt.objective/v1", requiredScopes: ["treeseed:read"] },
|
|
11
11
|
{ name: "governance-review", description: "Review a proposal or decision using current governance evidence.", argumentSchemaId: "treeseed.prompt.objective/v1", requiredScopes: ["treeseed:read"] },
|
|
12
12
|
{ name: "workday-planning", description: "Plan a time-based workday without bypassing API authority.", argumentSchemaId: "treeseed.prompt.objective/v1", requiredScopes: ["treeseed:read"] },
|
|
13
|
-
{ name: "project-agent-chat", description: "Prepare an explicit governed
|
|
13
|
+
{ name: "project-agent-chat", description: "Prepare an explicit governed team-topic chat invocation for project agents.", argumentSchemaId: "treeseed.prompt.objective/v1", requiredScopes: ["treeseed:read"] }
|
|
14
14
|
];
|
|
15
15
|
function resourcePath(restPath) {
|
|
16
16
|
const [root, version, ...segments] = restPath.split("/");
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const encryptedEnvelopeAadSchema: z.ZodObject<{
|
|
3
|
+
purpose: z.ZodString;
|
|
4
|
+
teamId: z.ZodOptional<z.ZodString>;
|
|
5
|
+
resourceType: z.ZodString;
|
|
6
|
+
resourceId: z.ZodString;
|
|
7
|
+
topicId: z.ZodOptional<z.ZodString>;
|
|
8
|
+
sendId: z.ZodOptional<z.ZodString>;
|
|
9
|
+
invocationId: z.ZodOptional<z.ZodString>;
|
|
10
|
+
assignmentId: z.ZodOptional<z.ZodString>;
|
|
11
|
+
sequence: z.ZodOptional<z.ZodNumber>;
|
|
12
|
+
eventType: z.ZodOptional<z.ZodString>;
|
|
13
|
+
schemaVersion: z.ZodString;
|
|
14
|
+
}, "strict", z.ZodTypeAny, {
|
|
15
|
+
purpose: string;
|
|
16
|
+
resourceType: string;
|
|
17
|
+
resourceId: string;
|
|
18
|
+
schemaVersion: string;
|
|
19
|
+
teamId?: string | undefined;
|
|
20
|
+
topicId?: string | undefined;
|
|
21
|
+
sendId?: string | undefined;
|
|
22
|
+
invocationId?: string | undefined;
|
|
23
|
+
assignmentId?: string | undefined;
|
|
24
|
+
sequence?: number | undefined;
|
|
25
|
+
eventType?: string | undefined;
|
|
26
|
+
}, {
|
|
27
|
+
purpose: string;
|
|
28
|
+
resourceType: string;
|
|
29
|
+
resourceId: string;
|
|
30
|
+
schemaVersion: string;
|
|
31
|
+
teamId?: string | undefined;
|
|
32
|
+
topicId?: string | undefined;
|
|
33
|
+
sendId?: string | undefined;
|
|
34
|
+
invocationId?: string | undefined;
|
|
35
|
+
assignmentId?: string | undefined;
|
|
36
|
+
sequence?: number | undefined;
|
|
37
|
+
eventType?: string | undefined;
|
|
38
|
+
}>;
|
|
39
|
+
export declare const encryptedEnvelopeSchema: z.ZodObject<{
|
|
40
|
+
schemaVersion: z.ZodLiteral<"treeseed.encrypted-envelope/v1">;
|
|
41
|
+
algorithm: z.ZodLiteral<"aes-256-gcm">;
|
|
42
|
+
keyProvider: z.ZodString;
|
|
43
|
+
keyId: z.ZodString;
|
|
44
|
+
keyVersion: z.ZodNumber;
|
|
45
|
+
wrappedDek: z.ZodObject<{
|
|
46
|
+
nonce: z.ZodString;
|
|
47
|
+
authenticationTag: z.ZodString;
|
|
48
|
+
ciphertext: z.ZodString;
|
|
49
|
+
}, "strict", z.ZodTypeAny, {
|
|
50
|
+
nonce: string;
|
|
51
|
+
authenticationTag: string;
|
|
52
|
+
ciphertext: string;
|
|
53
|
+
}, {
|
|
54
|
+
nonce: string;
|
|
55
|
+
authenticationTag: string;
|
|
56
|
+
ciphertext: string;
|
|
57
|
+
}>;
|
|
58
|
+
payload: z.ZodObject<{
|
|
59
|
+
nonce: z.ZodString;
|
|
60
|
+
authenticationTag: z.ZodString;
|
|
61
|
+
ciphertext: z.ZodString;
|
|
62
|
+
}, "strict", z.ZodTypeAny, {
|
|
63
|
+
nonce: string;
|
|
64
|
+
authenticationTag: string;
|
|
65
|
+
ciphertext: string;
|
|
66
|
+
}, {
|
|
67
|
+
nonce: string;
|
|
68
|
+
authenticationTag: string;
|
|
69
|
+
ciphertext: string;
|
|
70
|
+
}>;
|
|
71
|
+
aad: z.ZodObject<{
|
|
72
|
+
purpose: z.ZodString;
|
|
73
|
+
teamId: z.ZodOptional<z.ZodString>;
|
|
74
|
+
resourceType: z.ZodString;
|
|
75
|
+
resourceId: z.ZodString;
|
|
76
|
+
topicId: z.ZodOptional<z.ZodString>;
|
|
77
|
+
sendId: z.ZodOptional<z.ZodString>;
|
|
78
|
+
invocationId: z.ZodOptional<z.ZodString>;
|
|
79
|
+
assignmentId: z.ZodOptional<z.ZodString>;
|
|
80
|
+
sequence: z.ZodOptional<z.ZodNumber>;
|
|
81
|
+
eventType: z.ZodOptional<z.ZodString>;
|
|
82
|
+
schemaVersion: z.ZodString;
|
|
83
|
+
}, "strict", z.ZodTypeAny, {
|
|
84
|
+
purpose: string;
|
|
85
|
+
resourceType: string;
|
|
86
|
+
resourceId: string;
|
|
87
|
+
schemaVersion: string;
|
|
88
|
+
teamId?: string | undefined;
|
|
89
|
+
topicId?: string | undefined;
|
|
90
|
+
sendId?: string | undefined;
|
|
91
|
+
invocationId?: string | undefined;
|
|
92
|
+
assignmentId?: string | undefined;
|
|
93
|
+
sequence?: number | undefined;
|
|
94
|
+
eventType?: string | undefined;
|
|
95
|
+
}, {
|
|
96
|
+
purpose: string;
|
|
97
|
+
resourceType: string;
|
|
98
|
+
resourceId: string;
|
|
99
|
+
schemaVersion: string;
|
|
100
|
+
teamId?: string | undefined;
|
|
101
|
+
topicId?: string | undefined;
|
|
102
|
+
sendId?: string | undefined;
|
|
103
|
+
invocationId?: string | undefined;
|
|
104
|
+
assignmentId?: string | undefined;
|
|
105
|
+
sequence?: number | undefined;
|
|
106
|
+
eventType?: string | undefined;
|
|
107
|
+
}>;
|
|
108
|
+
aadDigest: z.ZodString;
|
|
109
|
+
ciphertextDigest: z.ZodString;
|
|
110
|
+
createdAt: z.ZodString;
|
|
111
|
+
}, "strict", z.ZodTypeAny, {
|
|
112
|
+
schemaVersion: "treeseed.encrypted-envelope/v1";
|
|
113
|
+
algorithm: "aes-256-gcm";
|
|
114
|
+
keyProvider: string;
|
|
115
|
+
keyId: string;
|
|
116
|
+
keyVersion: number;
|
|
117
|
+
wrappedDek: {
|
|
118
|
+
nonce: string;
|
|
119
|
+
authenticationTag: string;
|
|
120
|
+
ciphertext: string;
|
|
121
|
+
};
|
|
122
|
+
payload: {
|
|
123
|
+
nonce: string;
|
|
124
|
+
authenticationTag: string;
|
|
125
|
+
ciphertext: string;
|
|
126
|
+
};
|
|
127
|
+
aad: {
|
|
128
|
+
purpose: string;
|
|
129
|
+
resourceType: string;
|
|
130
|
+
resourceId: string;
|
|
131
|
+
schemaVersion: string;
|
|
132
|
+
teamId?: string | undefined;
|
|
133
|
+
topicId?: string | undefined;
|
|
134
|
+
sendId?: string | undefined;
|
|
135
|
+
invocationId?: string | undefined;
|
|
136
|
+
assignmentId?: string | undefined;
|
|
137
|
+
sequence?: number | undefined;
|
|
138
|
+
eventType?: string | undefined;
|
|
139
|
+
};
|
|
140
|
+
aadDigest: string;
|
|
141
|
+
ciphertextDigest: string;
|
|
142
|
+
createdAt: string;
|
|
143
|
+
}, {
|
|
144
|
+
schemaVersion: "treeseed.encrypted-envelope/v1";
|
|
145
|
+
algorithm: "aes-256-gcm";
|
|
146
|
+
keyProvider: string;
|
|
147
|
+
keyId: string;
|
|
148
|
+
keyVersion: number;
|
|
149
|
+
wrappedDek: {
|
|
150
|
+
nonce: string;
|
|
151
|
+
authenticationTag: string;
|
|
152
|
+
ciphertext: string;
|
|
153
|
+
};
|
|
154
|
+
payload: {
|
|
155
|
+
nonce: string;
|
|
156
|
+
authenticationTag: string;
|
|
157
|
+
ciphertext: string;
|
|
158
|
+
};
|
|
159
|
+
aad: {
|
|
160
|
+
purpose: string;
|
|
161
|
+
resourceType: string;
|
|
162
|
+
resourceId: string;
|
|
163
|
+
schemaVersion: string;
|
|
164
|
+
teamId?: string | undefined;
|
|
165
|
+
topicId?: string | undefined;
|
|
166
|
+
sendId?: string | undefined;
|
|
167
|
+
invocationId?: string | undefined;
|
|
168
|
+
assignmentId?: string | undefined;
|
|
169
|
+
sequence?: number | undefined;
|
|
170
|
+
eventType?: string | undefined;
|
|
171
|
+
};
|
|
172
|
+
aadDigest: string;
|
|
173
|
+
ciphertextDigest: string;
|
|
174
|
+
createdAt: string;
|
|
175
|
+
}>;
|
|
176
|
+
export type EncryptedEnvelopeAad = z.infer<typeof encryptedEnvelopeAadSchema>;
|
|
177
|
+
export type EncryptedEnvelope = z.infer<typeof encryptedEnvelopeSchema>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const base64Url = z.string().min(1).regex(/^[A-Za-z0-9_-]+$/u);
|
|
3
|
+
const digest = z.string().regex(/^sha256:[a-f0-9]{64}$/u);
|
|
4
|
+
const identifier = z.string().regex(/^[a-z][a-z0-9._-]{0,127}$/u);
|
|
5
|
+
const encryptedEnvelopeAadSchema = z.object({
|
|
6
|
+
purpose: identifier,
|
|
7
|
+
teamId: z.string().min(1).max(256).optional(),
|
|
8
|
+
resourceType: identifier,
|
|
9
|
+
resourceId: z.string().min(1).max(512),
|
|
10
|
+
topicId: z.string().min(1).max(256).optional(),
|
|
11
|
+
sendId: z.string().min(1).max(256).optional(),
|
|
12
|
+
invocationId: z.string().min(1).max(256).optional(),
|
|
13
|
+
assignmentId: z.string().min(1).max(256).optional(),
|
|
14
|
+
sequence: z.number().int().nonnegative().optional(),
|
|
15
|
+
eventType: z.string().min(1).max(256).optional(),
|
|
16
|
+
schemaVersion: z.string().min(1).max(128)
|
|
17
|
+
}).strict();
|
|
18
|
+
const encryptedEnvelopeSchema = z.object({
|
|
19
|
+
schemaVersion: z.literal("treeseed.encrypted-envelope/v1"),
|
|
20
|
+
algorithm: z.literal("aes-256-gcm"),
|
|
21
|
+
keyProvider: identifier,
|
|
22
|
+
keyId: identifier,
|
|
23
|
+
keyVersion: z.number().int().positive(),
|
|
24
|
+
wrappedDek: z.object({ nonce: base64Url, authenticationTag: base64Url, ciphertext: base64Url }).strict(),
|
|
25
|
+
payload: z.object({ nonce: base64Url, authenticationTag: base64Url, ciphertext: base64Url }).strict(),
|
|
26
|
+
aad: encryptedEnvelopeAadSchema,
|
|
27
|
+
aadDigest: digest,
|
|
28
|
+
ciphertextDigest: digest,
|
|
29
|
+
createdAt: z.string().datetime()
|
|
30
|
+
}).strict();
|
|
31
|
+
export {
|
|
32
|
+
encryptedEnvelopeAadSchema,
|
|
33
|
+
encryptedEnvelopeSchema
|
|
34
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type EncryptedEnvelope, type EncryptedEnvelopeAad } from './encrypted-envelope.js';
|
|
2
|
+
export interface EnvelopeKeyProvider {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
active(): {
|
|
5
|
+
id: string;
|
|
6
|
+
version: number;
|
|
7
|
+
key: Uint8Array;
|
|
8
|
+
};
|
|
9
|
+
resolve(id: string, version: number): Uint8Array | undefined;
|
|
10
|
+
}
|
|
11
|
+
export declare class EncryptedEnvelopeCodec {
|
|
12
|
+
private readonly keys;
|
|
13
|
+
constructor(keys: EnvelopeKeyProvider);
|
|
14
|
+
encrypt(plaintext: Uint8Array | string, aad: EncryptedEnvelopeAad): EncryptedEnvelope;
|
|
15
|
+
decrypt(input: EncryptedEnvelope): Buffer;
|
|
16
|
+
authenticate(input: EncryptedEnvelope): boolean;
|
|
17
|
+
rewrap(input: EncryptedEnvelope): EncryptedEnvelope;
|
|
18
|
+
}
|
|
19
|
+
export declare class StaticEnvelopeKeyProvider implements EnvelopeKeyProvider {
|
|
20
|
+
private readonly activeKey;
|
|
21
|
+
private readonly historical;
|
|
22
|
+
readonly id: string;
|
|
23
|
+
constructor(id: string, activeKey: {
|
|
24
|
+
id: string;
|
|
25
|
+
version: number;
|
|
26
|
+
key: Uint8Array;
|
|
27
|
+
}, historical?: Array<{
|
|
28
|
+
id: string;
|
|
29
|
+
version: number;
|
|
30
|
+
key: Uint8Array;
|
|
31
|
+
}>);
|
|
32
|
+
active(): {
|
|
33
|
+
id: string;
|
|
34
|
+
version: number;
|
|
35
|
+
key: Uint8Array;
|
|
36
|
+
};
|
|
37
|
+
resolve(id: string, version: number): Uint8Array<ArrayBufferLike> | undefined;
|
|
38
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import { encryptedEnvelopeSchema } from "./encrypted-envelope.js";
|
|
3
|
+
function canonical(value) {
|
|
4
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
5
|
+
if (value && typeof value === "object") return `{${Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${canonical(item)}`).join(",")}}`;
|
|
6
|
+
return JSON.stringify(value);
|
|
7
|
+
}
|
|
8
|
+
const digest = (value) => `sha256:${createHash("sha256").update(value).digest("hex")}`;
|
|
9
|
+
const encode = (value) => Buffer.from(value).toString("base64url");
|
|
10
|
+
const decode = (value) => {
|
|
11
|
+
const decoded = Buffer.from(value, "base64url");
|
|
12
|
+
if (decoded.toString("base64url") !== value) throw new Error("Encrypted envelope contains a non-canonical base64url value.");
|
|
13
|
+
return decoded;
|
|
14
|
+
};
|
|
15
|
+
function seal(key, plaintext, aad) {
|
|
16
|
+
if (key.byteLength !== 32) throw new Error("Envelope keys must contain exactly 32 bytes.");
|
|
17
|
+
const nonce = randomBytes(12);
|
|
18
|
+
const cipher = createCipheriv("aes-256-gcm", key, nonce);
|
|
19
|
+
cipher.setAAD(Buffer.from(aad));
|
|
20
|
+
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
21
|
+
return { nonce: encode(nonce), authenticationTag: encode(cipher.getAuthTag()), ciphertext: encode(ciphertext) };
|
|
22
|
+
}
|
|
23
|
+
function open(key, value, aad) {
|
|
24
|
+
const decipher = createDecipheriv("aes-256-gcm", key, decode(value.nonce));
|
|
25
|
+
decipher.setAAD(Buffer.from(aad));
|
|
26
|
+
decipher.setAuthTag(decode(value.authenticationTag));
|
|
27
|
+
return Buffer.concat([decipher.update(decode(value.ciphertext)), decipher.final()]);
|
|
28
|
+
}
|
|
29
|
+
class EncryptedEnvelopeCodec {
|
|
30
|
+
constructor(keys) {
|
|
31
|
+
this.keys = keys;
|
|
32
|
+
}
|
|
33
|
+
keys;
|
|
34
|
+
encrypt(plaintext, aad) {
|
|
35
|
+
const active = this.keys.active();
|
|
36
|
+
const aadValue = canonical(aad);
|
|
37
|
+
const dek = randomBytes(32);
|
|
38
|
+
const payload = seal(dek, typeof plaintext === "string" ? Buffer.from(plaintext) : plaintext, aadValue);
|
|
39
|
+
const wrappedDek = seal(active.key, dek, `${aadValue}:dek`);
|
|
40
|
+
return encryptedEnvelopeSchema.parse({
|
|
41
|
+
schemaVersion: "treeseed.encrypted-envelope/v1",
|
|
42
|
+
algorithm: "aes-256-gcm",
|
|
43
|
+
keyProvider: this.keys.id,
|
|
44
|
+
keyId: active.id,
|
|
45
|
+
keyVersion: active.version,
|
|
46
|
+
wrappedDek,
|
|
47
|
+
payload,
|
|
48
|
+
aad,
|
|
49
|
+
aadDigest: digest(aadValue),
|
|
50
|
+
ciphertextDigest: digest(decode(payload.ciphertext)),
|
|
51
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
decrypt(input) {
|
|
55
|
+
const envelope = encryptedEnvelopeSchema.parse(input);
|
|
56
|
+
const aad = canonical(envelope.aad);
|
|
57
|
+
if (digest(aad) !== envelope.aadDigest || digest(decode(envelope.payload.ciphertext)) !== envelope.ciphertextDigest) throw new Error("Encrypted envelope digest verification failed.");
|
|
58
|
+
const key = this.keys.resolve(envelope.keyId, envelope.keyVersion);
|
|
59
|
+
if (!key) throw new Error(`Encrypted envelope key ${envelope.keyId}:${envelope.keyVersion} is unavailable.`);
|
|
60
|
+
const dek = open(key, envelope.wrappedDek, `${aad}:dek`);
|
|
61
|
+
return open(dek, envelope.payload, aad);
|
|
62
|
+
}
|
|
63
|
+
authenticate(input) {
|
|
64
|
+
this.decrypt(input);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
rewrap(input) {
|
|
68
|
+
const envelope = encryptedEnvelopeSchema.parse(input);
|
|
69
|
+
const prior = this.keys.resolve(envelope.keyId, envelope.keyVersion);
|
|
70
|
+
if (!prior) throw new Error(`Encrypted envelope key ${envelope.keyId}:${envelope.keyVersion} is unavailable.`);
|
|
71
|
+
const aad = canonical(envelope.aad);
|
|
72
|
+
const dek = open(prior, envelope.wrappedDek, `${aad}:dek`);
|
|
73
|
+
const active = this.keys.active();
|
|
74
|
+
return encryptedEnvelopeSchema.parse({ ...envelope, keyProvider: this.keys.id, keyId: active.id, keyVersion: active.version, wrappedDek: seal(active.key, dek, `${aad}:dek`) });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
class StaticEnvelopeKeyProvider {
|
|
78
|
+
constructor(id, activeKey, historical = []) {
|
|
79
|
+
this.activeKey = activeKey;
|
|
80
|
+
this.historical = historical;
|
|
81
|
+
this.id = id;
|
|
82
|
+
}
|
|
83
|
+
activeKey;
|
|
84
|
+
historical;
|
|
85
|
+
id;
|
|
86
|
+
active() {
|
|
87
|
+
return this.activeKey;
|
|
88
|
+
}
|
|
89
|
+
resolve(id, version) {
|
|
90
|
+
return [this.activeKey, ...this.historical].find((entry) => entry.id === id && entry.version === version)?.key;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export {
|
|
94
|
+
EncryptedEnvelopeCodec,
|
|
95
|
+
StaticEnvelopeKeyProvider
|
|
96
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@treeseed/sdk",
|
|
3
|
-
"version": "0.13.0-rc.
|
|
3
|
+
"version": "0.13.0-rc.55",
|
|
4
4
|
"description": "Portable TreeSeed contracts, standards, and typed remote control-plane clients.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -62,6 +62,10 @@
|
|
|
62
62
|
"vitest": "^4.1.2"
|
|
63
63
|
},
|
|
64
64
|
"exports": {
|
|
65
|
+
"./security": {
|
|
66
|
+
"types": "./dist/security/index.d.ts",
|
|
67
|
+
"default": "./dist/security/index.js"
|
|
68
|
+
},
|
|
65
69
|
"./development": {
|
|
66
70
|
"types": "./dist/development/index.d.ts",
|
|
67
71
|
"default": "./dist/development/index.js"
|