@coffre/core 0.1.11 → 0.1.13
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/dist/access.d.ts +2 -12
- package/dist/access.js +4 -17
- package/dist/schemas.d.ts +1 -2
- package/dist/schemas.js +1 -2
- package/dist/vault.d.ts +18 -17
- package/dist/vault.js +10 -2
- package/package.json +2 -2
- package/src/access.ts +3 -23
- package/src/checkpoint.ts +10 -1
- package/src/schemas.ts +0 -1
- package/src/vault.ts +14 -15
package/dist/access.d.ts
CHANGED
|
@@ -85,16 +85,6 @@ export type Holdings = {
|
|
|
85
85
|
* `market/prod` does not let anyone rename `market`.
|
|
86
86
|
*/
|
|
87
87
|
export declare function allows(holder: Holdings, permission: Permission, place: Place): boolean;
|
|
88
|
-
/**
|
|
89
|
-
export declare function
|
|
90
|
-
/**
|
|
91
|
-
* Whether `actor` may set `subject`'s role at `place`, or take it away
|
|
92
|
-
* (`role` null). `grant.manage` on the project decides, with one addition:
|
|
93
|
-
* whoever may manage a project's environments may let a sync read an
|
|
94
|
-
* environment they can read themselves, and take that away again, since
|
|
95
|
-
* adding and removing syncs is theirs to do.
|
|
96
|
-
*/
|
|
97
|
-
export declare function mayManageAccess(actor: Holdings, subject: string, change: Place & {
|
|
98
|
-
role: Role | null;
|
|
99
|
-
}): boolean;
|
|
88
|
+
/** Whether the actor may manage grants on this project. */
|
|
89
|
+
export declare function mayManageAccess(actor: Holdings, place: Place): boolean;
|
|
100
90
|
//#endregion
|
package/dist/access.js
CHANGED
|
@@ -88,22 +88,9 @@ function allows(holder, permission, place) {
|
|
|
88
88
|
if (environmentId === null && holder.isOwner && PROJECT_ONLY_PERMISSIONS.includes(permission)) return true;
|
|
89
89
|
return holder.grants.some((grant) => grant.projectId === place.projectId && (grant.environmentId === null || grant.environmentId === environmentId) && roleGrants(grant.role, permission));
|
|
90
90
|
}
|
|
91
|
-
/**
|
|
92
|
-
function
|
|
93
|
-
return
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Whether `actor` may set `subject`'s role at `place`, or take it away
|
|
97
|
-
* (`role` null). `grant.manage` on the project decides, with one addition:
|
|
98
|
-
* whoever may manage a project's environments may let a sync read an
|
|
99
|
-
* environment they can read themselves, and take that away again, since
|
|
100
|
-
* adding and removing syncs is theirs to do.
|
|
101
|
-
*/
|
|
102
|
-
function mayManageAccess(actor, subject, change) {
|
|
103
|
-
if (allows(actor, "grant.manage", { projectId: change.projectId })) return true;
|
|
104
|
-
if (!isSyncPrincipal(subject) || !allows(actor, "environment.manage", { projectId: change.projectId })) return false;
|
|
105
|
-
if (change.role === null) return true;
|
|
106
|
-
return change.role === "viewer" && (change.environmentId ?? null) !== null && allows(actor, "secret.read", change);
|
|
91
|
+
/** Whether the actor may manage grants on this project. */
|
|
92
|
+
function mayManageAccess(actor, place) {
|
|
93
|
+
return allows(actor, "grant.manage", { projectId: place.projectId });
|
|
107
94
|
}
|
|
108
95
|
//#endregion
|
|
109
|
-
export { PERMISSIONS, PROJECT_ONLY_PERMISSIONS, ROLES, ROLE_NAMES, allows, assignableToEnvironment, isRole,
|
|
96
|
+
export { PERMISSIONS, PROJECT_ONLY_PERMISSIONS, ROLES, ROLE_NAMES, allows, assignableToEnvironment, isRole, mayManageAccess, roleGrants };
|
package/dist/schemas.d.ts
CHANGED
|
@@ -9,10 +9,9 @@ export declare const principalType: z.ZodEnum<{
|
|
|
9
9
|
}>;
|
|
10
10
|
export declare const principalId: z.ZodString;
|
|
11
11
|
export declare const instanceRole: z.ZodEnum<{
|
|
12
|
-
user: "user";
|
|
13
12
|
owner: "owner";
|
|
13
|
+
user: "user";
|
|
14
14
|
}>;
|
|
15
15
|
export declare const grantId: z.ZodString;
|
|
16
16
|
export declare const emailAddress: z.ZodString;
|
|
17
|
-
export declare const syncId: z.ZodString;
|
|
18
17
|
//#endregion
|
package/dist/schemas.js
CHANGED
|
@@ -8,6 +8,5 @@ const principalId = z.string().trim().min(1).max(320);
|
|
|
8
8
|
const instanceRole = z.enum(["user", "owner"]);
|
|
9
9
|
const grantId = z.string().uuid();
|
|
10
10
|
const emailAddress = z.string().email().max(320);
|
|
11
|
-
const syncId = z.string().uuid();
|
|
12
11
|
//#endregion
|
|
13
|
-
export { displayName, emailAddress, grantId, instanceRole, principalId, principalType, secretKey, slug
|
|
12
|
+
export { displayName, emailAddress, grantId, instanceRole, principalId, principalType, secretKey, slug };
|
package/dist/vault.d.ts
CHANGED
|
@@ -67,6 +67,12 @@ export declare function describeAccessFault(fault: AccessFault, names?: FaultNam
|
|
|
67
67
|
export declare function checkpointMessage(checkpoint: Omit<Checkpoint, 'signature' | 'keyId'>): Uint8Array<ArrayBuffer>;
|
|
68
68
|
/** Whether `checkpoint` was signed by the key whose raw public half is `publicKey`. */
|
|
69
69
|
export declare function verifyCheckpoint(checkpoint: Checkpoint, publicKey: string): Promise<boolean>;
|
|
70
|
+
/**
|
|
71
|
+
* `verifyCheckpoint` for many checkpoints signed by one key, the key
|
|
72
|
+
* imported once: a full verification checks every checkpoint, one every
|
|
73
|
+
* five minutes.
|
|
74
|
+
*/
|
|
75
|
+
export declare function checkpointVerifier(publicKey: string): Promise<(checkpoint: Checkpoint) => Promise<boolean>>;
|
|
70
76
|
//#endregion
|
|
71
77
|
//#region src/vault.d.ts
|
|
72
78
|
/**
|
|
@@ -162,11 +168,13 @@ export interface Vault {
|
|
|
162
168
|
*/
|
|
163
169
|
keyChecks(): Promise<KeyChecks>;
|
|
164
170
|
/**
|
|
165
|
-
* Check the whole log:
|
|
166
|
-
*
|
|
171
|
+
* Check the whole log: the vault's entries by their MACs, every
|
|
172
|
+
* checkpoint against the prefix it signed, the key batches accounted for,
|
|
167
173
|
* and replay it to see that who is a member, and what they hold, follow
|
|
168
|
-
* from it.
|
|
169
|
-
*
|
|
174
|
+
* from it. The links and hashes of the chain are the caller's to
|
|
175
|
+
* recompute through `upTo`, as the app does first; with none, the vault
|
|
176
|
+
* recomputes them all itself. A verdict and nothing else, so anyone may
|
|
177
|
+
* ask; the app asks for owners.
|
|
170
178
|
*/
|
|
171
179
|
verifyLog(input: VerifyLogInput): Promise<LogVerification>;
|
|
172
180
|
}
|
|
@@ -256,7 +264,7 @@ export type KeyChecks = {
|
|
|
256
264
|
})[];
|
|
257
265
|
};
|
|
258
266
|
/** Why someone reads: shown in the log, and the same rules apply to each. */
|
|
259
|
-
export type Purpose = 'reveal' | 'run' | 'compare'
|
|
267
|
+
export type Purpose = 'reveal' | 'run' | 'compare';
|
|
260
268
|
/** Ties the vault's entries to the app's request and audit rows. */
|
|
261
269
|
type Correlation = {
|
|
262
270
|
requestId?: string | null;
|
|
@@ -340,15 +348,6 @@ export type AdmitInput = Correlation & {
|
|
|
340
348
|
export type RemoveInput = Correlation & {
|
|
341
349
|
actor: string;
|
|
342
350
|
principal: string;
|
|
343
|
-
/**
|
|
344
|
-
* For a sync: the environment it pushes from. Whoever may manage it there
|
|
345
|
-
* may stop the sync, also once its grant there is revoked and only the
|
|
346
|
-
* one on its credential is left.
|
|
347
|
-
*/
|
|
348
|
-
source?: {
|
|
349
|
-
projectId: string;
|
|
350
|
-
environmentId: string;
|
|
351
|
-
};
|
|
352
351
|
};
|
|
353
352
|
/**
|
|
354
353
|
* A prefix of the log the vault signed: every entry up to `seq`, the last
|
|
@@ -381,9 +380,11 @@ export type LogHead = {
|
|
|
381
380
|
};
|
|
382
381
|
export type VerifyLogInput = {
|
|
383
382
|
/**
|
|
384
|
-
* The last entry the app verified,
|
|
385
|
-
*
|
|
386
|
-
*
|
|
383
|
+
* The last entry the app verified, every link and hash up to it
|
|
384
|
+
* recomputed and its own entries checked by its key: the vault checks the
|
|
385
|
+
* log still holds it, so between them both authors' entries are
|
|
386
|
+
* authenticated over the same prefix, and the vault leaves the links to
|
|
387
|
+
* the app.
|
|
387
388
|
*/
|
|
388
389
|
upTo?: LogHead | null;
|
|
389
390
|
};
|
package/dist/vault.js
CHANGED
|
@@ -6,8 +6,16 @@ function checkpointMessage(checkpoint) {
|
|
|
6
6
|
}
|
|
7
7
|
/** Whether `checkpoint` was signed by the key whose raw public half is `publicKey`. */
|
|
8
8
|
async function verifyCheckpoint(checkpoint, publicKey) {
|
|
9
|
+
return (await checkpointVerifier(publicKey))(checkpoint);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* `verifyCheckpoint` for many checkpoints signed by one key, the key
|
|
13
|
+
* imported once: a full verification checks every checkpoint, one every
|
|
14
|
+
* five minutes.
|
|
15
|
+
*/
|
|
16
|
+
async function checkpointVerifier(publicKey) {
|
|
9
17
|
const key = await crypto.subtle.importKey("raw", new Uint8Array(Buffer.from(publicKey, "base64")), { name: "Ed25519" }, false, ["verify"]);
|
|
10
|
-
return crypto.subtle.verify("Ed25519", key, new Uint8Array(Buffer.from(checkpoint.signature, "base64")), checkpointMessage(checkpoint));
|
|
18
|
+
return (checkpoint) => crypto.subtle.verify("Ed25519", key, new Uint8Array(Buffer.from(checkpoint.signature, "base64")), checkpointMessage(checkpoint));
|
|
11
19
|
}
|
|
12
20
|
//#endregion
|
|
13
21
|
//#region src/access-fault.ts
|
|
@@ -42,4 +50,4 @@ const ACCESS_ACTIONS = [
|
|
|
42
50
|
"access.revoke"
|
|
43
51
|
];
|
|
44
52
|
//#endregion
|
|
45
|
-
export { ACCESS_ACTIONS, checkpointMessage, describeAccessFault, verifyCheckpoint };
|
|
53
|
+
export { ACCESS_ACTIONS, checkpointMessage, checkpointVerifier, describeAccessFault, verifyCheckpoint };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coffre/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "What coffre's packages share: roles, envelopes, identity, the audit chain and the vault's contract.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"@types/node": "26.1.1",
|
|
70
70
|
"tsdown": "0.23.0",
|
|
71
71
|
"typescript": "5.9.3",
|
|
72
|
-
"@coffre/conformance": "0.1.
|
|
72
|
+
"@coffre/conformance": "0.1.13"
|
|
73
73
|
},
|
|
74
74
|
"scripts": {
|
|
75
75
|
"build": "tsdown",
|
package/src/access.ts
CHANGED
|
@@ -126,27 +126,7 @@ export function allows(holder: Holdings, permission: Permission, place: Place):
|
|
|
126
126
|
);
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
/**
|
|
130
|
-
export function
|
|
131
|
-
return
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Whether `actor` may set `subject`'s role at `place`, or take it away
|
|
136
|
-
* (`role` null). `grant.manage` on the project decides, with one addition:
|
|
137
|
-
* whoever may manage a project's environments may let a sync read an
|
|
138
|
-
* environment they can read themselves, and take that away again, since
|
|
139
|
-
* adding and removing syncs is theirs to do.
|
|
140
|
-
*/
|
|
141
|
-
export function mayManageAccess(
|
|
142
|
-
actor: Holdings,
|
|
143
|
-
subject: string,
|
|
144
|
-
change: Place & { role: Role | null },
|
|
145
|
-
): boolean {
|
|
146
|
-
if (allows(actor, 'grant.manage', { projectId: change.projectId })) return true;
|
|
147
|
-
if (!isSyncPrincipal(subject) || !allows(actor, 'environment.manage', { projectId: change.projectId })) {
|
|
148
|
-
return false;
|
|
149
|
-
}
|
|
150
|
-
if (change.role === null) return true;
|
|
151
|
-
return change.role === 'viewer' && (change.environmentId ?? null) !== null && allows(actor, 'secret.read', change);
|
|
129
|
+
/** Whether the actor may manage grants on this project. */
|
|
130
|
+
export function mayManageAccess(actor: Holdings, place: Place): boolean {
|
|
131
|
+
return allows(actor, 'grant.manage', { projectId: place.projectId });
|
|
152
132
|
}
|
package/src/checkpoint.ts
CHANGED
|
@@ -8,6 +8,15 @@ export function checkpointMessage(checkpoint: Omit<Checkpoint, 'signature' | 'ke
|
|
|
8
8
|
|
|
9
9
|
/** Whether `checkpoint` was signed by the key whose raw public half is `publicKey`. */
|
|
10
10
|
export async function verifyCheckpoint(checkpoint: Checkpoint, publicKey: string): Promise<boolean> {
|
|
11
|
+
return (await checkpointVerifier(publicKey))(checkpoint);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* `verifyCheckpoint` for many checkpoints signed by one key, the key
|
|
16
|
+
* imported once: a full verification checks every checkpoint, one every
|
|
17
|
+
* five minutes.
|
|
18
|
+
*/
|
|
19
|
+
export async function checkpointVerifier(publicKey: string): Promise<(checkpoint: Checkpoint) => Promise<boolean>> {
|
|
11
20
|
const key = await crypto.subtle.importKey('raw', new Uint8Array(Buffer.from(publicKey, 'base64')), { name: 'Ed25519' }, false, ['verify']);
|
|
12
|
-
return crypto.subtle.verify('Ed25519', key, new Uint8Array(Buffer.from(checkpoint.signature, 'base64')), checkpointMessage(checkpoint));
|
|
21
|
+
return (checkpoint) => crypto.subtle.verify('Ed25519', key, new Uint8Array(Buffer.from(checkpoint.signature, 'base64')), checkpointMessage(checkpoint));
|
|
13
22
|
}
|
package/src/schemas.ts
CHANGED
package/src/vault.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AccessFault } from './access-fault.ts';
|
|
2
2
|
import type { Role } from './access.ts';
|
|
3
3
|
|
|
4
|
-
export { checkpointMessage, verifyCheckpoint } from './checkpoint.ts';
|
|
4
|
+
export { checkpointMessage, checkpointVerifier, verifyCheckpoint } from './checkpoint.ts';
|
|
5
5
|
export { describeAccessFault, type AccessFault, type FaultGrant, type FaultNames } from './access-fault.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -77,11 +77,13 @@ export interface Vault {
|
|
|
77
77
|
*/
|
|
78
78
|
keyChecks(): Promise<KeyChecks>;
|
|
79
79
|
/**
|
|
80
|
-
* Check the whole log:
|
|
81
|
-
*
|
|
80
|
+
* Check the whole log: the vault's entries by their MACs, every
|
|
81
|
+
* checkpoint against the prefix it signed, the key batches accounted for,
|
|
82
82
|
* and replay it to see that who is a member, and what they hold, follow
|
|
83
|
-
* from it.
|
|
84
|
-
*
|
|
83
|
+
* from it. The links and hashes of the chain are the caller's to
|
|
84
|
+
* recompute through `upTo`, as the app does first; with none, the vault
|
|
85
|
+
* recomputes them all itself. A verdict and nothing else, so anyone may
|
|
86
|
+
* ask; the app asks for owners.
|
|
85
87
|
*/
|
|
86
88
|
verifyLog(input: VerifyLogInput): Promise<LogVerification>;
|
|
87
89
|
}
|
|
@@ -158,7 +160,7 @@ export type WrappedKey = { kekProvider: string; kekId: string; kekVersion: strin
|
|
|
158
160
|
export type KeyChecks = { current: { kekProvider: string; kekId: string }; checks: (WrappedKey & { seq: number })[] };
|
|
159
161
|
|
|
160
162
|
/** Why someone reads: shown in the log, and the same rules apply to each. */
|
|
161
|
-
export type Purpose = 'reveal' | 'run' | 'compare'
|
|
163
|
+
export type Purpose = 'reveal' | 'run' | 'compare';
|
|
162
164
|
|
|
163
165
|
/** Ties the vault's entries to the app's request and audit rows. */
|
|
164
166
|
type Correlation = {
|
|
@@ -245,12 +247,7 @@ export type AdmitInput = Correlation & {
|
|
|
245
247
|
export type RemoveInput = Correlation & {
|
|
246
248
|
actor: string;
|
|
247
249
|
principal: string;
|
|
248
|
-
|
|
249
|
-
* For a sync: the environment it pushes from. Whoever may manage it there
|
|
250
|
-
* may stop the sync, also once its grant there is revoked and only the
|
|
251
|
-
* one on its credential is left.
|
|
252
|
-
*/
|
|
253
|
-
source?: { projectId: string; environmentId: string };
|
|
250
|
+
|
|
254
251
|
};
|
|
255
252
|
|
|
256
253
|
/**
|
|
@@ -281,9 +278,11 @@ export type LogHead = { seq: number; hash: string };
|
|
|
281
278
|
|
|
282
279
|
export type VerifyLogInput = {
|
|
283
280
|
/**
|
|
284
|
-
* The last entry the app verified,
|
|
285
|
-
*
|
|
286
|
-
*
|
|
281
|
+
* The last entry the app verified, every link and hash up to it
|
|
282
|
+
* recomputed and its own entries checked by its key: the vault checks the
|
|
283
|
+
* log still holds it, so between them both authors' entries are
|
|
284
|
+
* authenticated over the same prefix, and the vault leaves the links to
|
|
285
|
+
* the app.
|
|
287
286
|
*/
|
|
288
287
|
upTo?: LogHead | null;
|
|
289
288
|
};
|