@totemsdk/omnia-host 0.13.0 → 0.13.1

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/index.d.ts CHANGED
@@ -9,6 +9,10 @@ export { createHostIdentityAndManifest, createHostManifest, loadHostIdentity, }
9
9
  export { SqliteChannelStore } from './stores/sqlite-store.js';
10
10
  export { OperationStore } from './stores/operations.js';
11
11
  export type { OperationRecord, OperationStatus } from './stores/operations.js';
12
+ export { SqliteAuditStore, createAuditRecorder } from './stores/audit.js';
13
+ export type { SignerAuditEntry } from './stores/audit.js';
14
+ export { generateTotp, verifyTotp, mfaOperationHash, verifyMfaAssertion, assertMfaAssertion, MemoryMfaReplayGuard, } from './mfa.js';
15
+ export type { MfaAssertion, MfaChallenge, MfaReplayGuard, MfaVerificationOptions, MfaVerificationResult, TotpOptions, } from './mfa.js';
12
16
  export { createTotemNodeAdapter } from './chain/totem-node-adapter.js';
13
17
  export type { TotemNodeAdapter, TotemNodeAdapterOptions, ConfirmationOptions } from './chain/totem-node-adapter.js';
14
18
  export { InProcessRoutingProvider } from './router/routing-provider.js';
@@ -20,5 +24,5 @@ export type { OperationStoreLike } from './api/methods.js';
20
24
  export { DisabledAnalyticsStore, DuckDbAnalyticsStore, initializeDuckDb } from './stores/analytics.js';
21
25
  export type { AnalyticsEvent, AnalyticsStore, DuckDbConnection } from './stores/analytics.js';
22
26
  export { createLocalSignerPort, createPolicySignerPort } from './signer-port.js';
23
- export type { SignerPolicy } from './signer-port.js';
27
+ export type { SignerAuditRecord, SignerPolicy } from './signer-port.js';
24
28
  export * from './observe/index.js';
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.createPolicySignerPort = exports.createLocalSignerPort = exports.initializeDuckDb = exports.DuckDbAnalyticsStore = exports.DisabledAnalyticsStore = exports.createHostMethods = exports.GoRoutingProvider = exports.InProcessRoutingProvider = exports.createTotemNodeAdapter = exports.OperationStore = exports.SqliteChannelStore = exports.loadHostIdentity = exports.createHostManifest = exports.createHostIdentityAndManifest = exports.encryptSeedForKeyfile = exports.leaseStorageDir = exports.JsonFileStorageAdapter = exports.hasSigningMaterial = exports.createHostSigning = exports.createOmniaHost = exports.loadConfigFromEnv = void 0;
17
+ exports.createPolicySignerPort = exports.createLocalSignerPort = exports.initializeDuckDb = exports.DuckDbAnalyticsStore = exports.DisabledAnalyticsStore = exports.createHostMethods = exports.GoRoutingProvider = exports.InProcessRoutingProvider = exports.createTotemNodeAdapter = exports.MemoryMfaReplayGuard = exports.assertMfaAssertion = exports.verifyMfaAssertion = exports.mfaOperationHash = exports.verifyTotp = exports.generateTotp = exports.createAuditRecorder = exports.SqliteAuditStore = exports.OperationStore = exports.SqliteChannelStore = exports.loadHostIdentity = exports.createHostManifest = exports.createHostIdentityAndManifest = exports.encryptSeedForKeyfile = exports.leaseStorageDir = exports.JsonFileStorageAdapter = exports.hasSigningMaterial = exports.createHostSigning = exports.createOmniaHost = exports.loadConfigFromEnv = void 0;
18
18
  var config_js_1 = require("./config.js");
19
19
  Object.defineProperty(exports, "loadConfigFromEnv", { enumerable: true, get: function () { return config_js_1.loadConfigFromEnv; } });
20
20
  var lifecycle_js_1 = require("./lifecycle.js");
@@ -33,6 +33,16 @@ var sqlite_store_js_1 = require("./stores/sqlite-store.js");
33
33
  Object.defineProperty(exports, "SqliteChannelStore", { enumerable: true, get: function () { return sqlite_store_js_1.SqliteChannelStore; } });
34
34
  var operations_js_1 = require("./stores/operations.js");
35
35
  Object.defineProperty(exports, "OperationStore", { enumerable: true, get: function () { return operations_js_1.OperationStore; } });
36
+ var audit_js_1 = require("./stores/audit.js");
37
+ Object.defineProperty(exports, "SqliteAuditStore", { enumerable: true, get: function () { return audit_js_1.SqliteAuditStore; } });
38
+ Object.defineProperty(exports, "createAuditRecorder", { enumerable: true, get: function () { return audit_js_1.createAuditRecorder; } });
39
+ var mfa_js_1 = require("./mfa.js");
40
+ Object.defineProperty(exports, "generateTotp", { enumerable: true, get: function () { return mfa_js_1.generateTotp; } });
41
+ Object.defineProperty(exports, "verifyTotp", { enumerable: true, get: function () { return mfa_js_1.verifyTotp; } });
42
+ Object.defineProperty(exports, "mfaOperationHash", { enumerable: true, get: function () { return mfa_js_1.mfaOperationHash; } });
43
+ Object.defineProperty(exports, "verifyMfaAssertion", { enumerable: true, get: function () { return mfa_js_1.verifyMfaAssertion; } });
44
+ Object.defineProperty(exports, "assertMfaAssertion", { enumerable: true, get: function () { return mfa_js_1.assertMfaAssertion; } });
45
+ Object.defineProperty(exports, "MemoryMfaReplayGuard", { enumerable: true, get: function () { return mfa_js_1.MemoryMfaReplayGuard; } });
36
46
  var totem_node_adapter_js_1 = require("./chain/totem-node-adapter.js");
37
47
  Object.defineProperty(exports, "createTotemNodeAdapter", { enumerable: true, get: function () { return totem_node_adapter_js_1.createTotemNodeAdapter; } });
38
48
  var routing_provider_js_1 = require("./router/routing-provider.js");
package/dist/mfa.d.ts ADDED
@@ -0,0 +1,77 @@
1
+ /**
2
+ * TOTP + operation-bound MFA assertions (RFC-061 D8).
3
+ *
4
+ * The node/host creates an MfaChallenge for a money-moving operation; the user
5
+ * returns an MfaAssertion (TOTP proof). Verification binds the assertion to the
6
+ * exact operation hash, disclosure and expiry, and consumes it once. Secrets
7
+ * never leave the verifier.
8
+ */
9
+ export interface TotpOptions {
10
+ /** Current time in ms (default Date.now()). */
11
+ time?: number;
12
+ period?: number;
13
+ digits?: number;
14
+ /** Accepted step window on either side (default 1). */
15
+ window?: number;
16
+ }
17
+ export declare function generateTotp(secret: string | Uint8Array, options?: TotpOptions): string;
18
+ export declare function verifyTotp(secret: string | Uint8Array, code: string, options?: TotpOptions): boolean;
19
+ export type MfaPurpose = 'sign' | 'submit' | 'key-rotation' | 'recovery' | 'admin';
20
+ export type MfaMethod = 'totp' | 'webauthn' | 'recovery-code';
21
+ /** Server-issued challenge bound to one exact operation. */
22
+ export interface MfaChallenge {
23
+ challengeId: string;
24
+ purpose: MfaPurpose;
25
+ operationHash: string;
26
+ disclosureId?: string;
27
+ expiresAt: number;
28
+ attempts?: number;
29
+ }
30
+ /** Client-supplied proof for a challenge. */
31
+ export interface MfaAssertion {
32
+ challengeId: string;
33
+ method: MfaMethod;
34
+ proof: string;
35
+ issuedAt: number;
36
+ }
37
+ /** Single-use replay guard (durable in a hosted deployment). */
38
+ export interface MfaReplayGuard {
39
+ has(challengeId: string): boolean;
40
+ add(challengeId: string): void;
41
+ }
42
+ export declare class MemoryMfaReplayGuard implements MfaReplayGuard {
43
+ private readonly used;
44
+ has(challengeId: string): boolean;
45
+ add(challengeId: string): void;
46
+ }
47
+ export interface MfaVerificationOptions {
48
+ secret: string | Uint8Array;
49
+ /** Expected operation hash (see `mfaOperationHash`). */
50
+ operationHash: string;
51
+ disclosureId?: string;
52
+ now?: number;
53
+ period?: number;
54
+ digits?: number;
55
+ window?: number;
56
+ /** Tolerated future clock skew for issuedAt (default 5 minutes). */
57
+ clockSkewMs?: number;
58
+ /** Replay guard; defaults to a process-local guard when omitted. */
59
+ used?: MfaReplayGuard;
60
+ }
61
+ export interface MfaVerificationResult {
62
+ ok: boolean;
63
+ reason?: string;
64
+ }
65
+ export declare function verifyMfaAssertion(challenge: MfaChallenge, assertion: MfaAssertion, options: MfaVerificationOptions): MfaVerificationResult;
66
+ export declare function assertMfaAssertion(challenge: MfaChallenge, assertion: MfaAssertion, options: MfaVerificationOptions): void;
67
+ export interface MfaOperationInput {
68
+ purpose: MfaPurpose;
69
+ domain: string;
70
+ /** Canonical operation digest (SignRequest.digest). */
71
+ digest: Uint8Array | string;
72
+ disclosureId?: string;
73
+ amount?: string;
74
+ asset?: string;
75
+ }
76
+ /** Stable operation hash binding an MFA challenge to one signing intent. */
77
+ export declare function mfaOperationHash(input: MfaOperationInput): string;
package/dist/mfa.js ADDED
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ /**
3
+ * TOTP + operation-bound MFA assertions (RFC-061 D8).
4
+ *
5
+ * The node/host creates an MfaChallenge for a money-moving operation; the user
6
+ * returns an MfaAssertion (TOTP proof). Verification binds the assertion to the
7
+ * exact operation hash, disclosure and expiry, and consumes it once. Secrets
8
+ * never leave the verifier.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.MemoryMfaReplayGuard = void 0;
12
+ exports.generateTotp = generateTotp;
13
+ exports.verifyTotp = verifyTotp;
14
+ exports.verifyMfaAssertion = verifyMfaAssertion;
15
+ exports.assertMfaAssertion = assertMfaAssertion;
16
+ exports.mfaOperationHash = mfaOperationHash;
17
+ const node_crypto_1 = require("node:crypto");
18
+ const BASE32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
19
+ const DEFAULT_PERIOD = 30;
20
+ const DEFAULT_DIGITS = 6;
21
+ function toSecret(secret) {
22
+ if (secret instanceof Uint8Array)
23
+ return secret;
24
+ const clean = secret.toUpperCase().replace(/[\s-]/g, '').replace(/=+$/, '');
25
+ if (!clean)
26
+ throw new Error('MFA secret is empty');
27
+ let bits = 0;
28
+ let value = 0;
29
+ const out = [];
30
+ for (const ch of clean) {
31
+ const index = BASE32.indexOf(ch);
32
+ if (index < 0)
33
+ throw new Error('MFA secret is not valid base32');
34
+ value = (value << 5) | index;
35
+ bits += 5;
36
+ if (bits >= 8) {
37
+ out.push((value >>> (bits - 8)) & 0xff);
38
+ bits -= 8;
39
+ }
40
+ }
41
+ return Uint8Array.from(out);
42
+ }
43
+ function hotp(secret, counter, digits) {
44
+ const msg = Buffer.alloc(8);
45
+ msg.writeBigUInt64BE(BigInt(counter));
46
+ const digest = (0, node_crypto_1.createHmac)('sha1', secret).update(msg).digest();
47
+ const offset = digest[digest.length - 1] & 0x0f;
48
+ const binary = ((digest[offset] & 0x7f) << 24) |
49
+ ((digest[offset + 1] & 0xff) << 16) |
50
+ ((digest[offset + 2] & 0xff) << 8) |
51
+ (digest[offset + 3] & 0xff);
52
+ return String(binary % 10 ** digits).padStart(digits, '0');
53
+ }
54
+ function step(timeMs, period) {
55
+ return Math.floor(timeMs / 1000 / period);
56
+ }
57
+ function generateTotp(secret, options = {}) {
58
+ const period = options.period ?? DEFAULT_PERIOD;
59
+ const digits = options.digits ?? DEFAULT_DIGITS;
60
+ const time = options.time ?? Date.now();
61
+ return hotp(toSecret(secret), step(time, period), digits);
62
+ }
63
+ function verifyTotp(secret, code, options = {}) {
64
+ const period = options.period ?? DEFAULT_PERIOD;
65
+ const digits = options.digits ?? DEFAULT_DIGITS;
66
+ const window = options.window ?? 1;
67
+ const time = options.time ?? Date.now();
68
+ const normalized = code.replace(/\s+/g, '');
69
+ if (!/^\d+$/.test(normalized) || normalized.length !== digits)
70
+ return false;
71
+ const key = toSecret(secret);
72
+ const current = step(time, period);
73
+ for (let delta = -window; delta <= window; delta += 1) {
74
+ const candidate = hotp(key, current + delta, digits);
75
+ if (candidate.length === normalized.length &&
76
+ (0, node_crypto_1.timingSafeEqual)(Buffer.from(candidate), Buffer.from(normalized))) {
77
+ return true;
78
+ }
79
+ }
80
+ return false;
81
+ }
82
+ class MemoryMfaReplayGuard {
83
+ used = new Set();
84
+ has(challengeId) {
85
+ return this.used.has(challengeId);
86
+ }
87
+ add(challengeId) {
88
+ this.used.add(challengeId);
89
+ }
90
+ }
91
+ exports.MemoryMfaReplayGuard = MemoryMfaReplayGuard;
92
+ const defaultGuard = new MemoryMfaReplayGuard();
93
+ function verifyMfaAssertion(challenge, assertion, options) {
94
+ const now = options.now ?? Date.now();
95
+ if (!challenge.challengeId || assertion.challengeId !== challenge.challengeId) {
96
+ return { ok: false, reason: 'MFA assertion does not match the challenge' };
97
+ }
98
+ if (challenge.expiresAt <= now)
99
+ return { ok: false, reason: 'MFA challenge has expired' };
100
+ if (challenge.attempts !== undefined && challenge.attempts <= 0) {
101
+ return { ok: false, reason: 'MFA challenge has no attempts left' };
102
+ }
103
+ if (challenge.operationHash !== options.operationHash) {
104
+ return { ok: false, reason: 'MFA challenge does not match the operation' };
105
+ }
106
+ if (challenge.disclosureId !== undefined && challenge.disclosureId !== options.disclosureId) {
107
+ return { ok: false, reason: 'MFA challenge does not match the disclosure' };
108
+ }
109
+ const skew = options.clockSkewMs ?? 5 * 60_000;
110
+ if (!Number.isFinite(assertion.issuedAt) ||
111
+ assertion.issuedAt <= 0 ||
112
+ assertion.issuedAt > now + skew) {
113
+ return { ok: false, reason: 'MFA assertion has an invalid issuedAt' };
114
+ }
115
+ const used = options.used ?? defaultGuard;
116
+ if (used.has(challenge.challengeId)) {
117
+ return { ok: false, reason: 'MFA assertion has already been used' };
118
+ }
119
+ if (assertion.method !== 'totp') {
120
+ return { ok: false, reason: `unsupported MFA method: ${assertion.method}` };
121
+ }
122
+ if (!verifyTotp(options.secret, assertion.proof, {
123
+ time: assertion.issuedAt,
124
+ period: options.period,
125
+ digits: options.digits,
126
+ window: options.window,
127
+ })) {
128
+ return { ok: false, reason: 'MFA proof is invalid' };
129
+ }
130
+ used.add(challenge.challengeId);
131
+ return { ok: true };
132
+ }
133
+ function assertMfaAssertion(challenge, assertion, options) {
134
+ const result = verifyMfaAssertion(challenge, assertion, options);
135
+ if (!result.ok)
136
+ throw new Error(result.reason ?? 'MFA verification failed');
137
+ }
138
+ /** Stable operation hash binding an MFA challenge to one signing intent. */
139
+ function mfaOperationHash(input) {
140
+ const digest = input.digest instanceof Uint8Array
141
+ ? Buffer.from(input.digest).toString('hex')
142
+ : String(input.digest).replace(/^0x/i, '').toLowerCase();
143
+ const canonical = JSON.stringify({
144
+ purpose: input.purpose,
145
+ domain: input.domain,
146
+ digest,
147
+ disclosureId: input.disclosureId ?? null,
148
+ amount: input.amount ?? null,
149
+ asset: input.asset ?? null,
150
+ });
151
+ return '0x' + (0, node_crypto_1.createHash)('sha256').update(canonical, 'utf8').digest('hex');
152
+ }
@@ -40,7 +40,7 @@ function channelView(channel) {
40
40
  currentSequence: channel.currentSequence,
41
41
  fundingCoinId: channel.fundingCoinId,
42
42
  fundingTxId: channel.fundingTxId,
43
- parties: channel.parties,
43
+ parties: toJson(channel.parties),
44
44
  balances: Object.fromEntries(Object.entries(channel.balances).map(([k, v]) => [k, v.toString()])),
45
45
  pendingHTLCs: (channel.pendingHTLCs ?? []).map((h) => toJson(h)),
46
46
  updatedAt: channel.updatedAt,
@@ -54,7 +54,7 @@ function factoryView(factory) {
54
54
  status: f.status,
55
55
  tokenId: f.tokenId,
56
56
  tokenScale: f.tokenScale ?? 0,
57
- participants: f.participants,
57
+ participants: toJson(f.participants),
58
58
  allocations: Object.fromEntries(Object.entries(allocations).map(([k, v]) => [k, v.toString()])),
59
59
  updatedAt: f.updatedAt,
60
60
  };
@@ -72,12 +72,12 @@ function operationsView(operations) {
72
72
  continue;
73
73
  if (r.operationId)
74
74
  seen.add(r.operationId);
75
- out.push({
75
+ out.push(toJson({
76
76
  operationId: r.operationId,
77
77
  status: record.status ?? status,
78
78
  updatedAt: record.updatedAt,
79
79
  error: record.error,
80
- });
80
+ }));
81
81
  }
82
82
  }
83
83
  return out;
@@ -5,9 +5,24 @@
5
5
  * contract. A hosted deployment supplies its own `SignerPort` (KMS/HSM/MPC +
6
6
  * policy) instead; the host code is agnostic.
7
7
  */
8
- import { type CustodyMode, type SignDomain, type SignRequest, type SignerPort } from '@totemsdk/omnia';
8
+ import { type CustodyAcknowledgement, type CustodyMode, type SignDomain, type SignRequest, type SignerPort } from '@totemsdk/omnia';
9
9
  import type { HostSigning } from './signing.js';
10
10
  export declare function createLocalSignerPort(signing: HostSigning): SignerPort;
11
+ /** Durable audit record for every policy-signer decision (RFC-061 D6). */
12
+ export interface SignerAuditRecord {
13
+ requestId: string;
14
+ domain: SignDomain;
15
+ mode: CustodyMode;
16
+ disclosureId: string;
17
+ acknowledgementId?: string;
18
+ signer: string;
19
+ decision: 'allow' | 'deny';
20
+ reason?: string;
21
+ amount?: string;
22
+ summary?: string;
23
+ occurredAt?: number;
24
+ mfaVerified?: boolean;
25
+ }
11
26
  /** Hosted/KMS policy checks applied before delegating to the underlying port. */
12
27
  export interface SignerPolicy {
13
28
  allowedDomains?: SignDomain[];
@@ -16,6 +31,24 @@ export interface SignerPolicy {
16
31
  maxAmount?: bigint;
17
32
  /** Require a disclosure hash/version on the custody context. */
18
33
  requireDisclosure?: boolean;
34
+ /** RFC-061 signer gate: exact disclosure acknowledgement + policy decision. */
35
+ custody?: {
36
+ acknowledgement?: CustodyAcknowledgement;
37
+ subjectId?: string;
38
+ policyDecision?: 'allow' | 'deny';
39
+ now?: number;
40
+ };
41
+ /** Durable audit sink; called with allow/deny decisions. */
42
+ audit?: (record: SignerAuditRecord) => void | Promise<void>;
43
+ /**
44
+ * RFC-061 D8: verify a request-scoped, operation-bound MFA assertion.
45
+ * Defaults to non-self custody; a `minAmount` can scope it to larger moves.
46
+ */
47
+ mfa?: {
48
+ verify: (request: SignRequest) => void | Promise<void>;
49
+ modes?: CustodyMode[];
50
+ minAmount?: bigint;
51
+ };
19
52
  /** Additional operator approval hook (KMS/HSM/MPC or quorum). */
20
53
  authorize?: (request: SignRequest) => void | Promise<void>;
21
54
  }
@@ -56,6 +56,32 @@ function createPolicySignerPort(base, policy = {}) {
56
56
  if (!request.signers.includes(base.publicKeyDigest)) {
57
57
  throw new Error(`signer ${base.publicKeyDigest} is not a required signer for ${request.requestId}`);
58
58
  }
59
+ const ack = policy.custody?.acknowledgement;
60
+ const auditBase = {
61
+ requestId: request.requestId,
62
+ domain: request.domain,
63
+ mode: request.custody.mode,
64
+ disclosureId: (0, omnia_1.custodyDisclosureId)(request.custody),
65
+ ...(ack
66
+ ? { acknowledgementId: `${ack.disclosureId}:${ack.subjectId}:${ack.acknowledgedAt}` }
67
+ : {}),
68
+ signer: base.publicKeyDigest,
69
+ amount: request.context.amount,
70
+ summary: request.context.summary,
71
+ occurredAt: Date.now(),
72
+ };
73
+ try {
74
+ (0, omnia_1.assertCustodyAllowsSigning)(request, policy.custody ?? {});
75
+ }
76
+ catch (err) {
77
+ await policy.audit?.({
78
+ ...auditBase,
79
+ decision: 'deny',
80
+ mfaVerified: false,
81
+ reason: err instanceof Error ? err.message : String(err),
82
+ });
83
+ throw err;
84
+ }
59
85
  if (policy.allowedDomains && !policy.allowedDomains.includes(request.domain)) {
60
86
  throw new Error(`domain ${request.domain} is not allowed by policy`);
61
87
  }
@@ -72,8 +98,34 @@ function createPolicySignerPort(base, policy = {}) {
72
98
  throw new Error(`request amount ${parsed} exceeds policy limit ${policy.maxAmount}`);
73
99
  }
74
100
  }
101
+ let mfaVerified = false;
102
+ if (policy.mfa) {
103
+ const modes = policy.mfa.modes ?? ['trust-minimised', 'hosted'];
104
+ const modeRequired = modes.includes(request.custody.mode);
105
+ let amountRequired = true;
106
+ if (policy.mfa.minAmount !== undefined && request.context.amount !== undefined) {
107
+ amountRequired = BigInt(request.context.amount) >= policy.mfa.minAmount;
108
+ }
109
+ if (modeRequired && amountRequired) {
110
+ try {
111
+ await policy.mfa.verify(request);
112
+ mfaVerified = true;
113
+ }
114
+ catch (err) {
115
+ await policy.audit?.({
116
+ ...auditBase,
117
+ decision: 'deny',
118
+ mfaVerified: false,
119
+ reason: err instanceof Error ? err.message : String(err),
120
+ });
121
+ throw err;
122
+ }
123
+ }
124
+ }
75
125
  await policy.authorize?.(request);
76
- return base.sign(request);
126
+ const signature = await base.sign(request);
127
+ await policy.audit?.({ ...auditBase, decision: 'allow', mfaVerified });
128
+ return signature;
77
129
  },
78
130
  };
79
131
  }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Durable signer audit store (RFC-061 D6).
3
+ *
4
+ * Every allow/deny decision from `createPolicySignerPort` is appended here
5
+ * through `@totemsdk/storage` (SyncSqliteStore; same codec/envelope as the
6
+ * host's other local state). Audit records never contain keys, seeds,
7
+ * preimages or raw signatures.
8
+ */
9
+ import type { SignerAuditRecord } from '../signer-port.js';
10
+ export interface SignerAuditEntry extends SignerAuditRecord {
11
+ auditId: string;
12
+ recordedAt: number;
13
+ }
14
+ /** Storage-backed, append-only signer audit log. */
15
+ export declare class SqliteAuditStore {
16
+ private readonly kv;
17
+ constructor(path?: string);
18
+ append(record: SignerAuditRecord): SignerAuditEntry;
19
+ /** Newest first. */
20
+ list(limit?: number): SignerAuditEntry[];
21
+ close(): void;
22
+ }
23
+ /** Adapt the store to the `SignerPolicy.audit` callback. */
24
+ export declare function createAuditRecorder(store: SqliteAuditStore): (record: SignerAuditRecord) => SignerAuditEntry;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ /**
3
+ * Durable signer audit store (RFC-061 D6).
4
+ *
5
+ * Every allow/deny decision from `createPolicySignerPort` is appended here
6
+ * through `@totemsdk/storage` (SyncSqliteStore; same codec/envelope as the
7
+ * host's other local state). Audit records never contain keys, seeds,
8
+ * preimages or raw signatures.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.SqliteAuditStore = void 0;
12
+ exports.createAuditRecorder = createAuditRecorder;
13
+ const node_crypto_1 = require("node:crypto");
14
+ const sqlite_sync_1 = require("@totemsdk/storage/sqlite-sync");
15
+ const PREFIX = 'omnia:audit:';
16
+ /** Storage-backed, append-only signer audit log. */
17
+ class SqliteAuditStore {
18
+ kv;
19
+ constructor(path = ':memory:') {
20
+ this.kv = new sqlite_sync_1.SyncSqliteStore(path);
21
+ }
22
+ append(record) {
23
+ const recordedAt = Date.now();
24
+ const auditId = (0, node_crypto_1.randomUUID)();
25
+ const entry = { ...record, auditId, recordedAt };
26
+ this.kv.set(`${PREFIX}${String(recordedAt).padStart(13, '0')}:${auditId}`, entry);
27
+ return entry;
28
+ }
29
+ /** Newest first. */
30
+ list(limit = 100) {
31
+ const safeLimit = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 100;
32
+ return this.kv
33
+ .keys()
34
+ .filter((key) => key.startsWith(PREFIX))
35
+ .sort()
36
+ .reverse()
37
+ .slice(0, safeLimit)
38
+ .map((key) => this.kv.get(key))
39
+ .filter((entry) => entry !== null);
40
+ }
41
+ close() {
42
+ this.kv.close();
43
+ }
44
+ }
45
+ exports.SqliteAuditStore = SqliteAuditStore;
46
+ /** Adapt the store to the `SignerPolicy.audit` callback. */
47
+ function createAuditRecorder(store) {
48
+ return (record) => store.append(record);
49
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@totemsdk/omnia-host",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "description": "Durable Omnia node daemon for channel lifecycle, routing, and control APIs",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,21 +22,21 @@
22
22
  ],
23
23
  "dependencies": {
24
24
  "ws": "^8.21.1",
25
- "@totemsdk/chain-provider": "^0.3.0",
26
25
  "@totemsdk/core": "^1.4.0",
26
+ "@totemsdk/chain-provider": "^0.3.0",
27
27
  "@totemsdk/identity": "^1.0.2",
28
28
  "@totemsdk/liquidity-bond": "^0.3.1",
29
29
  "@totemsdk/manifest": "^2.0.2",
30
30
  "@totemsdk/minima-rpc": "^0.4.0",
31
31
  "@totemsdk/omnia": "^2.3.1",
32
- "@totemsdk/omnia-pool": "^0.5.3",
33
32
  "@totemsdk/omnia-factory": "^0.7.0",
34
33
  "@totemsdk/omnia-router": "^0.4.0",
35
34
  "@totemsdk/omnia-splice": "^0.3.0",
36
- "@totemsdk/omnia-vtxo": "^0.6.1",
37
- "@totemsdk/wots-lease": "^1.0.1",
35
+ "@totemsdk/p2p-hub": "^0.1.0",
38
36
  "@totemsdk/storage": "^0.2.1",
39
- "@totemsdk/p2p-hub": "^0.1.0"
37
+ "@totemsdk/omnia-vtxo": "^0.6.1",
38
+ "@totemsdk/omnia-pool": "^0.5.3",
39
+ "@totemsdk/wots-lease": "^1.0.1"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/jest": "^30.0.0",