@volter/world-core 2.0.28 → 2.0.30
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/src/credential.d.ts +16 -0
- package/dist/src/credential.js +15 -0
- package/dist/src/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/credential.ts +21 -1
- package/src/index.ts +1 -1
package/dist/src/credential.d.ts
CHANGED
|
@@ -58,6 +58,17 @@ export declare class MemoryCredentialStorage implements CredentialStorage {
|
|
|
58
58
|
}
|
|
59
59
|
export declare function sealCredential(kekSecret: string, payload: CredentialPayload, placedAt: string, slot?: string, bound?: string): Promise<SealedUnderKey>;
|
|
60
60
|
export declare function openSealedCredential(kekSecret: string, sealed: SealedCredential, slot?: string, bound?: string): Promise<CredentialPayload>;
|
|
61
|
+
/** What a rewrap made of a record: its new form, current (already under the newest version), or not this sealer's to
|
|
62
|
+
* vouch for (another key's, or an answer whose version it could not read): only `current` says nothing is left. */
|
|
63
|
+
export type Rewrapped = {
|
|
64
|
+
status: 'rewrapped';
|
|
65
|
+
sealed: SealedCredential;
|
|
66
|
+
} | {
|
|
67
|
+
status: 'current';
|
|
68
|
+
} | {
|
|
69
|
+
status: 'foreign';
|
|
70
|
+
reason: string;
|
|
71
|
+
};
|
|
61
72
|
/** Seals and opens credentials. A host chooses one (`setSealer`); a key it holds (`keySealer`), or a vault that holds
|
|
62
73
|
* the key and wraps for it (`transitSealer`). */
|
|
63
74
|
export interface Sealer {
|
|
@@ -65,6 +76,11 @@ export interface Sealer {
|
|
|
65
76
|
seal(payload: CredentialPayload, placedAt: string, slot?: string, bound?: string): Promise<SealedCredential>;
|
|
66
77
|
/** `bound`: the destination it is opened for; a record bound elsewhere refuses */
|
|
67
78
|
open(sealed: SealedCredential, slot?: string, bound?: string): Promise<CredentialPayload>;
|
|
79
|
+
/** The record with its DEK wrapped under the newest version of its key, without the DEK leaving the sealer (a
|
|
80
|
+
* vault's `transit/rewrap` after a rotation): `rewrapped` with its new form, `current` when it already is, `foreign`
|
|
81
|
+
* when it is not this sealer's to vouch for. What an operator's sweep runs after rotating a key, before raising its
|
|
82
|
+
* minimum decryption version. */
|
|
83
|
+
rewrap?(sealed: SealedCredential): Promise<Rewrapped>;
|
|
68
84
|
}
|
|
69
85
|
/** Wraps under a key the host holds (the user's key file, a deployment secret): `v: 1` records. */
|
|
70
86
|
export declare function keySealer(kekSecret: () => string): Sealer;
|
package/dist/src/credential.js
CHANGED
|
@@ -257,5 +257,20 @@ export function transitSealer(options) {
|
|
|
257
257
|
throw new Error('credential unseal failed: wrong slot or corrupted record');
|
|
258
258
|
}
|
|
259
259
|
},
|
|
260
|
+
async rewrap(sealed) {
|
|
261
|
+
// only this sealer's own records: a key's (v1) or another transit key's are not its to rewrap
|
|
262
|
+
if (sealed.v !== 2)
|
|
263
|
+
return { status: 'foreign', reason: 'sealed under a host key' };
|
|
264
|
+
if (sealed.transitKey !== named)
|
|
265
|
+
return { status: 'foreign', reason: `sealed by ${sealed.transitKey}` };
|
|
266
|
+
const { ciphertext } = await call('rewrap', { ciphertext: sealed.wrappedDek });
|
|
267
|
+
const version = (wrapped) => { const m = typeof wrapped === 'string' ? /^vault:v(\d+):/.exec(wrapped) : null; return m ? Number(m[1]) : null; };
|
|
268
|
+
const before = version(sealed.wrappedDek);
|
|
269
|
+
const after = version(ciphertext);
|
|
270
|
+
if (before === null || after === null)
|
|
271
|
+
return { status: 'foreign', reason: 'a wrapped key whose version could not be read' };
|
|
272
|
+
// the vault rewraps to a fresh ciphertext at the same version when the key has not moved: current, nothing written
|
|
273
|
+
return after > before ? { status: 'rewrapped', sealed: { ...sealed, wrappedDek: String(ciphertext) } } : { status: 'current' };
|
|
274
|
+
},
|
|
260
275
|
};
|
|
261
276
|
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -48,7 +48,7 @@ export * as git from './git/index.js';
|
|
|
48
48
|
export { PLACEHOLDER_REMOTE, beginPlaceholderPull, endPlaceholderPull, placeholderPullActive, placeholderPullMarkerPath, withPlaceholderPull, placeholderEventsFor } from './placeholder-remote.js';
|
|
49
49
|
export { ownFields, subjectHistory, rebaseBranch, cutCheckpoint, appendParentEntry, wholeLog, branchEntries, branchLogPath, branchMetaPath, CHECKPOINT_EVERY, dropCheckpoint, foldEntries, landedCopy, landedIds, parentEntries, parentLogPath, position, readBranchMeta, readTree, toEntry, toEvent, unpushedEntries, writeBranchMeta } from './log.js';
|
|
50
50
|
export type { BranchMeta, Entry, EntryOp, Receipt } from './log.js';
|
|
51
|
-
export { sealCredential, openSealedCredential, MemoryCredentialStorage, keySealer, transitSealer, type Sealer, type TransitSealerOptions, type SealedByVault, type SealedUnderKey } from './credential.js';
|
|
51
|
+
export { sealCredential, openSealedCredential, MemoryCredentialStorage, keySealer, transitSealer, type Rewrapped, type Sealer, type TransitSealerOptions, type SealedByVault, type SealedUnderKey } from './credential.js';
|
|
52
52
|
export type { CredentialPayload, CredentialStorage, SealedCredential } from './credential.js';
|
|
53
53
|
export { validateRemoteOrigin, buildRemoteExecute, type CredentialCustody } from './executor.js';
|
|
54
54
|
export type { TwinAuthStrategy } from './executor.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/world-core",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.30",
|
|
4
4
|
"description": "The kernel of Volter World: one log per twin, branches as pointers, checkpoints, the fold that keeps a twin current, the head that performs a write against the vendor, references, and the git plane. A twin package builds on it; the runtime serves it.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twin",
|
package/src/credential.ts
CHANGED
|
@@ -178,6 +178,10 @@ export async function openSealedCredential(kekSecret: string, sealed: SealedCred
|
|
|
178
178
|
|
|
179
179
|
// ── a sealer: what seals and opens a host's credentials ─────────────────────────────────────
|
|
180
180
|
|
|
181
|
+
/** What a rewrap made of a record: its new form, current (already under the newest version), or not this sealer's to
|
|
182
|
+
* vouch for (another key's, or an answer whose version it could not read): only `current` says nothing is left. */
|
|
183
|
+
export type Rewrapped = { status: 'rewrapped'; sealed: SealedCredential } | { status: 'current' } | { status: 'foreign'; reason: string };
|
|
184
|
+
|
|
181
185
|
/** Seals and opens credentials. A host chooses one (`setSealer`); a key it holds (`keySealer`), or a vault that holds
|
|
182
186
|
* the key and wraps for it (`transitSealer`). */
|
|
183
187
|
export interface Sealer {
|
|
@@ -185,6 +189,11 @@ export interface Sealer {
|
|
|
185
189
|
seal(payload: CredentialPayload, placedAt: string, slot?: string, bound?: string): Promise<SealedCredential>;
|
|
186
190
|
/** `bound`: the destination it is opened for; a record bound elsewhere refuses */
|
|
187
191
|
open(sealed: SealedCredential, slot?: string, bound?: string): Promise<CredentialPayload>;
|
|
192
|
+
/** The record with its DEK wrapped under the newest version of its key, without the DEK leaving the sealer (a
|
|
193
|
+
* vault's `transit/rewrap` after a rotation): `rewrapped` with its new form, `current` when it already is, `foreign`
|
|
194
|
+
* when it is not this sealer's to vouch for. What an operator's sweep runs after rotating a key, before raising its
|
|
195
|
+
* minimum decryption version. */
|
|
196
|
+
rewrap?(sealed: SealedCredential): Promise<Rewrapped>;
|
|
188
197
|
}
|
|
189
198
|
|
|
190
199
|
/** Wraps under a key the host holds (the user's key file, a deployment secret): `v: 1` records. */
|
|
@@ -262,7 +271,7 @@ export function transitSealer(options: TransitSealerOptions): Sealer {
|
|
|
262
271
|
if (status >= 500) renewedAt = 0;
|
|
263
272
|
if (status >= 300) console.warn(`[credential] the vault at ${addr} did not renew this host's token (${status})`);
|
|
264
273
|
};
|
|
265
|
-
const call = async (op: 'encrypt' | 'decrypt' | 'hmac', body: Record<string, string | number>, key = options.key): Promise<Record<string, string>> => {
|
|
274
|
+
const call = async (op: 'encrypt' | 'decrypt' | 'hmac' | 'rewrap', body: Record<string, string | number>, key = options.key): Promise<Record<string, string>> => {
|
|
266
275
|
const which = `${mount}/${key}`;
|
|
267
276
|
let response: Response;
|
|
268
277
|
try {
|
|
@@ -328,5 +337,16 @@ export function transitSealer(options: TransitSealerOptions): Sealer {
|
|
|
328
337
|
throw new Error('credential unseal failed: wrong slot or corrupted record');
|
|
329
338
|
}
|
|
330
339
|
},
|
|
340
|
+
async rewrap(sealed) {
|
|
341
|
+
// only this sealer's own records: a key's (v1) or another transit key's are not its to rewrap
|
|
342
|
+
if (sealed.v !== 2) return { status: 'foreign', reason: 'sealed under a host key' };
|
|
343
|
+
if (sealed.transitKey !== named) return { status: 'foreign', reason: `sealed by ${sealed.transitKey}` };
|
|
344
|
+
const { ciphertext } = await call('rewrap', { ciphertext: sealed.wrappedDek });
|
|
345
|
+
const version = (wrapped: unknown): number | null => { const m = typeof wrapped === 'string' ? /^vault:v(\d+):/.exec(wrapped) : null; return m ? Number(m[1]) : null; };
|
|
346
|
+
const before = version(sealed.wrappedDek); const after = version(ciphertext);
|
|
347
|
+
if (before === null || after === null) return { status: 'foreign', reason: 'a wrapped key whose version could not be read' };
|
|
348
|
+
// the vault rewraps to a fresh ciphertext at the same version when the key has not moved: current, nothing written
|
|
349
|
+
return after > before ? { status: 'rewrapped', sealed: { ...sealed, wrappedDek: String(ciphertext) } } : { status: 'current' };
|
|
350
|
+
},
|
|
331
351
|
};
|
|
332
352
|
}
|
package/src/index.ts
CHANGED
|
@@ -296,7 +296,7 @@ export { PLACEHOLDER_REMOTE, beginPlaceholderPull, endPlaceholderPull, placehold
|
|
|
296
296
|
|
|
297
297
|
export { ownFields, subjectHistory, rebaseBranch, cutCheckpoint, appendParentEntry, wholeLog, branchEntries, branchLogPath, branchMetaPath, CHECKPOINT_EVERY, dropCheckpoint, foldEntries, landedCopy, landedIds, parentEntries, parentLogPath, position, readBranchMeta, readTree, toEntry, toEvent, unpushedEntries, writeBranchMeta } from './log.ts';
|
|
298
298
|
export type { BranchMeta, Entry, EntryOp, Receipt } from './log.ts';
|
|
299
|
-
export { sealCredential, openSealedCredential, MemoryCredentialStorage, keySealer, transitSealer, type Sealer, type TransitSealerOptions, type SealedByVault, type SealedUnderKey } from './credential.ts';
|
|
299
|
+
export { sealCredential, openSealedCredential, MemoryCredentialStorage, keySealer, transitSealer, type Rewrapped, type Sealer, type TransitSealerOptions, type SealedByVault, type SealedUnderKey } from './credential.ts';
|
|
300
300
|
export type { CredentialPayload, CredentialStorage, SealedCredential } from './credential.ts';
|
|
301
301
|
export { validateRemoteOrigin, buildRemoteExecute, type CredentialCustody } from './executor.ts';
|
|
302
302
|
export type { TwinAuthStrategy } from './executor.ts';
|