@totemsdk/omnia-host 0.2.1 → 0.3.0

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 CHANGED
@@ -38,12 +38,13 @@ The daemon configuration is read from environment variables:
38
38
 
39
39
  ### Signing model
40
40
 
41
- `OMNIA_HOST_SEED` (or the keyfile) is the base seed. The host derives a
42
- per-address seed (`derivePerAddressSeed(baseSeed, localAddressIndex)`) and
43
- signs every channel state with the flat WOTS key at index 0 of that seed; the
44
- channel participant's `publicKeyDigest` is `derivePKdigest(perAddressSeed, 0)`.
45
- The WOTS lease journal and watermark persist in `<OMNIA_HOST_DB>.lease/` so
46
- they survive restarts together with the channel database.
41
+ `OMNIA_HOST_SEED` (or the keyfile) is the base seed. The host derives the unified
42
+ child TreeKey (`createUnifiedChildTreeKey(baseSeed, localAddressIndex)`) and signs
43
+ every channel state (and the boot manifest) with serialized TreeSignatures; the
44
+ channel participant's `publicKeyDigest` is that TreeKey's root public key, and the
45
+ identity address is the matching Minima address. The WOTS lease journal and
46
+ watermark persist in `<OMNIA_HOST_DB>.lease/` so they survive restarts together
47
+ with the channel database.
47
48
 
48
49
  ### Read-only mode
49
50
 
@@ -24,6 +24,18 @@ export interface ControlServerOptions {
24
24
  isReady?: () => boolean;
25
25
  methods?: Map<string, JsonRpcHandler>;
26
26
  onConnection?: (socket: WebSocket) => void;
27
+ /**
28
+ * Browser Origin allowlist (AUD-009). When set, browser requests whose
29
+ * `Origin` header is not listed are rejected. Requests with no Origin
30
+ * (native clients, curl) are always allowed.
31
+ */
32
+ allowedOrigins?: string[];
33
+ /**
34
+ * When set, control-plane requests must present this bearer token (AUD-009):
35
+ * `Authorization: Bearer <token>` over HTTP, or a query param / subprotocol
36
+ * over the WebSocket handshake.
37
+ */
38
+ authToken?: string;
27
39
  }
28
40
  export interface ControlServer {
29
41
  listen(): Promise<void>;
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createControlServer = createControlServer;
7
+ const node_crypto_1 = __importDefault(require("node:crypto"));
7
8
  const node_http_1 = __importDefault(require("node:http"));
8
9
  const ws_1 = require("ws");
9
10
  function response(id, result) {
@@ -44,23 +45,95 @@ function sendJson(res, status, body) {
44
45
  res.writeHead(status, { 'content-type': 'application/json', 'content-length': Buffer.byteLength(payload) });
45
46
  res.end(payload);
46
47
  }
48
+ function pathname(url) {
49
+ if (!url)
50
+ return '/';
51
+ const query = url.indexOf('?');
52
+ return query === -1 ? url : url.slice(0, query);
53
+ }
54
+ function isOriginAllowed(req, allowedOrigins) {
55
+ const origin = req.headers.origin;
56
+ // Native clients (no browser Origin header) are always allowed.
57
+ if (!origin)
58
+ return true;
59
+ // RFC-020 H1: browser requests require an explicit allowlist (default deny).
60
+ if (!allowedOrigins || allowedOrigins.length === 0)
61
+ return false;
62
+ return allowedOrigins.includes(origin);
63
+ }
64
+ function bearerToken(req) {
65
+ const header = req.headers.authorization;
66
+ if (typeof header === 'string' && header.startsWith('Bearer '))
67
+ return header.slice('Bearer '.length);
68
+ return undefined;
69
+ }
70
+ function handshakeToken(req) {
71
+ const url = new URL(req.url ?? '/', 'http://localhost');
72
+ const queryToken = url.searchParams.get('token');
73
+ if (queryToken)
74
+ return queryToken;
75
+ const header = req.headers['sec-websocket-protocol'];
76
+ const raw = Array.isArray(header) ? header.join(',') : header;
77
+ if (typeof raw === 'string') {
78
+ for (const part of raw.split(',')) {
79
+ const protocol = part.trim();
80
+ if (protocol.startsWith('bearer.'))
81
+ return protocol.slice('bearer.'.length);
82
+ if (protocol)
83
+ return protocol;
84
+ }
85
+ }
86
+ return undefined;
87
+ }
88
+ function safeTokenEqual(provided, expected) {
89
+ if (provided === undefined)
90
+ return false;
91
+ const a = Buffer.from(provided);
92
+ const b = Buffer.from(expected);
93
+ if (a.length !== b.length)
94
+ return false;
95
+ return node_crypto_1.default.timingSafeEqual(a, b);
96
+ }
47
97
  function createControlServer(options) {
98
+ // RFC-020 H1: a non-loopback bind must be authenticated. Refuse to start a
99
+ // network-exposed control plane without a token.
100
+ const isLoopback = options.host === '127.0.0.1' || options.host === '::1' || options.host === 'localhost';
101
+ if (!isLoopback && (options.authToken === undefined || options.authToken.length === 0)) {
102
+ throw new Error(`Refusing to bind the control plane to ${options.host} without a control token (OMNIA_CONTROL_TOKEN)`);
103
+ }
48
104
  const methods = options.methods ?? new Map();
49
105
  const wsPath = options.wsPath ?? '/rpc';
106
+ const allowedOrigins = options.allowedOrigins;
107
+ const authToken = options.authToken;
50
108
  const server = node_http_1.default.createServer(async (req, res) => {
51
- if (req.method === 'GET' && req.url === '/healthz') {
109
+ if (req.method === 'GET' && pathname(req.url) === '/healthz') {
52
110
  sendJson(res, 200, { ok: true });
53
111
  return;
54
112
  }
55
- if (req.method === 'GET' && req.url === '/readyz') {
113
+ if (req.method === 'GET' && pathname(req.url) === '/readyz') {
56
114
  const ready = options.isReady?.() ?? false;
57
115
  sendJson(res, ready ? 200 : 503, { ok: ready });
58
116
  return;
59
117
  }
60
- if (req.method !== 'POST' || req.url !== wsPath) {
118
+ if (req.method !== 'POST' || pathname(req.url) !== wsPath) {
61
119
  sendJson(res, 404, { error: 'Not found' });
62
120
  return;
63
121
  }
122
+ if (!isOriginAllowed(req, allowedOrigins)) {
123
+ sendJson(res, 403, { error: 'Forbidden' });
124
+ return;
125
+ }
126
+ // RFC-020 H1: require application/json — a browser cross-origin `text/plain`
127
+ // POST (a classic CSRF vector that skips CORS preflight) must be rejected.
128
+ const contentType = String(req.headers['content-type'] ?? '').toLowerCase();
129
+ if (!contentType.startsWith('application/json')) {
130
+ sendJson(res, 415, { error: 'Unsupported Media Type: application/json required' });
131
+ return;
132
+ }
133
+ if (authToken !== undefined && !safeTokenEqual(bearerToken(req), authToken)) {
134
+ sendJson(res, 403, { error: 'Forbidden' });
135
+ return;
136
+ }
64
137
  try {
65
138
  const request = JSON.parse(await readBody(req));
66
139
  sendJson(res, 200, await dispatch(request, methods));
@@ -72,7 +145,15 @@ function createControlServer(options) {
72
145
  });
73
146
  const webSockets = new ws_1.WebSocketServer({ noServer: true });
74
147
  server.on('upgrade', (req, socket, head) => {
75
- if (req.url !== wsPath) {
148
+ if (pathname(req.url) !== wsPath) {
149
+ socket.destroy();
150
+ return;
151
+ }
152
+ if (!isOriginAllowed(req, allowedOrigins)) {
153
+ socket.destroy();
154
+ return;
155
+ }
156
+ if (authToken !== undefined && !safeTokenEqual(handshakeToken(req), authToken)) {
76
157
  socket.destroy();
77
158
  return;
78
159
  }
package/dist/config.d.ts CHANGED
@@ -26,6 +26,16 @@ export interface OmniaHostConfig {
26
26
  identityFile?: string;
27
27
  /** EdgeServiceManifest serviceType (default "omnia-router"). */
28
28
  serviceType: string;
29
+ /**
30
+ * Browser Origin allowlist for the control plane (OMNIA_ALLOWED_ORIGINS,
31
+ * comma-separated). Undefined/empty allows any Origin.
32
+ */
33
+ allowedOrigins?: string[];
34
+ /**
35
+ * Bearer token required on control-plane requests (OMNIA_CONTROL_TOKEN).
36
+ * Undefined disables token auth.
37
+ */
38
+ controlToken?: string;
29
39
  }
30
40
  /** Load the daemon configuration from the documented OMNIA_* environment. */
31
41
  export declare function loadConfigFromEnv(env?: NodeJS.ProcessEnv): OmniaHostConfig;
package/dist/config.js CHANGED
@@ -28,6 +28,13 @@ function optionalInteger(value) {
28
28
  throw new Error(`OMNIA_LOCAL_ADDRESS_INDEX must be a non-negative integer; received ${value}`);
29
29
  return parsed;
30
30
  }
31
+ function parseOrigins(value) {
32
+ const parts = value
33
+ ?.split(',')
34
+ .map((part) => part.trim())
35
+ .filter((part) => part.length > 0);
36
+ return parts && parts.length > 0 ? parts : undefined;
37
+ }
31
38
  function parseSeed(value) {
32
39
  const seed = optional(value);
33
40
  if (!seed)
@@ -76,5 +83,7 @@ function loadConfigFromEnv(env = process.env) {
76
83
  readOnly: env.OMNIA_HOST_READ_ONLY?.trim() === '1',
77
84
  identityFile: optional(env.OMNIA_HOST_IDENTITY_FILE),
78
85
  serviceType: optional(env.OMNIA_HOST_SERVICE_TYPE) ?? 'omnia-router',
86
+ allowedOrigins: parseOrigins(env.OMNIA_ALLOWED_ORIGINS),
87
+ controlToken: optional(env.OMNIA_CONTROL_TOKEN),
79
88
  };
80
89
  }
package/dist/identity.js CHANGED
@@ -92,7 +92,22 @@ async function createHostManifest(config, signing, identity) {
92
92
  capabilities: ['omnia:open-channel', 'omnia:update-channel', 'omnia:route', 'omnia:settle'],
93
93
  tags: ['omnia', 'payment-channel', 'daemon'],
94
94
  };
95
- return (0, manifest_1.signManifest)(manifest, signing.perAddressSeed, 0);
95
+ // RFC-037: sign the manifest with the unified child TreeKey. Allocate a
96
+ // one-time leaf via the lease so restarts never reuse a WOTS leaf.
97
+ const reservation = await signing.leaseProvider.reserveKeyUse({
98
+ treeId: 'omnia-manifest',
99
+ purpose: 'manifest',
100
+ });
101
+ const useIndex = reservation.indices.l1 * 64 + reservation.indices.l2;
102
+ try {
103
+ const signed = await (0, manifest_1.signManifest)(manifest, signing.treeKey, useIndex);
104
+ await signing.leaseProvider.commitKeyUse(reservation.reservationId, 'manifest');
105
+ return signed;
106
+ }
107
+ catch (err) {
108
+ await signing.leaseProvider.burnReservation(reservation.reservationId, 'manifest-sign-failed').catch(() => { });
109
+ throw err;
110
+ }
96
111
  }
97
112
  /** Boot-time identity + manifest assembly. */
98
113
  async function createHostIdentityAndManifest(config, signing) {
package/dist/lifecycle.js CHANGED
@@ -86,6 +86,8 @@ function createOmniaHost(config, dependencies = {}) {
86
86
  port: config.port,
87
87
  wsPath: config.wsPath,
88
88
  isReady: () => started,
89
+ allowedOrigins: config.allowedOrigins,
90
+ authToken: config.controlToken,
89
91
  methods: dependencies.methods ?? (0, methods_js_1.createHostMethods)({
90
92
  channels: channelStore,
91
93
  routing: routingProvider,
package/dist/signing.d.ts CHANGED
@@ -2,20 +2,21 @@
2
2
  * Host signing material — derives the channel signer, WOTS lease provider,
3
3
  * and Minima address from env-provisioned key material.
4
4
  *
5
- * Key model (matches @totemsdk/omnia's verification path):
5
+ * Key model (RFC-037 unified WOTS):
6
6
  * - `baseSeed` comes from OMNIA_HOST_SEED (BIP39 mnemonic or 32-byte hex)
7
7
  * or from an OMNIA_HOST_KEYFILE JSON.
8
- * - `perAddressSeed = derivePerAddressSeed(baseSeed, localAddressIndex)`.
9
- * - The channel participant's `publicKeyDigest` is the flat WOTS PK digest
10
- * `derivePKdigest(perAddressSeed, 0)` — omnia verifies signatures with
11
- * `wotsVerifyDigest(sig, digest, pkDigest)`, which is key-index agnostic,
12
- * so the signer always signs with key index 0 of the per-address seed.
13
- * - The lease indices {addressIndex, l1, l2} are slot bookkeeping for
14
- * double-sign prevention (watermark monotonicity), not key derivation.
8
+ * - The channel key is the unified child TreeKey
9
+ * `createUnifiedChildTreeKey(baseSeed, localAddressIndex)`; the participant's
10
+ * `publicKeyDigest` is its tree-root public key — the same key the on-chain
11
+ * `MULTISIG` eltoo script embeds.
12
+ * - Signatures are serialized hierarchical `TreeSignature`s (`serializeTreeSignature`)
13
+ * over the reserved leaf `(l1*64 + l2)`, verified by `verifyChannelSignature`.
14
+ * - The lease indices {addressIndex, l1, l2} select the one-time leaf.
15
15
  *
16
16
  * The lease journal/watermark persist in a directory next to the channel DB
17
17
  * (`<dbPath>.lease/`) so they survive restarts together with the channel store.
18
18
  */
19
+ import { type TreeKey } from '@totemsdk/core';
19
20
  import type { StorageAdapter } from '@totemsdk/core';
20
21
  import type { ChannelSigner } from '@totemsdk/omnia';
21
22
  import type { WotsLeaseProvider } from '@totemsdk/wots-lease';
@@ -23,12 +24,14 @@ import type { OmniaHostConfig } from './config.js';
23
24
  export interface HostSigning {
24
25
  signer: ChannelSigner;
25
26
  leaseProvider: WotsLeaseProvider;
27
+ /** Minima address for the unified child TreeKey (identity + channels). */
26
28
  address: string;
29
+ /** Channel participant key: the unified child TreeKey root pubkey (RFC-037). */
27
30
  publicKeyDigest: string;
28
31
  /** 32-byte base seed (used by the identity/manifest layer). */
29
32
  baseSeed: Uint8Array;
30
- /** Per-address seed used for channel signing (key index 0). */
31
- perAddressSeed: Uint8Array;
33
+ /** Unified child TreeKey used for channel + manifest signing (RFC-037). */
34
+ treeKey: TreeKey;
32
35
  addressIndex: number;
33
36
  deviceId: string;
34
37
  }
@@ -43,13 +46,15 @@ interface KeyfilePayload {
43
46
  export declare function encryptSeedForKeyfile(seed: Uint8Array, passphrase: string): KeyfilePayload;
44
47
  /**
45
48
  * JSON-file-backed StorageAdapter rooted at a directory next to the channel DB.
46
- * Each key is stored as `<key>.json`; the directory is created on first write.
49
+ *
50
+ * Delegates to the hardened shared `FileStore` from `@totemsdk/storage/fs`
51
+ * (RFC-007): one versioned codec record per key written temp+rename with an
52
+ * fsync; corrupt records surface as `StorageError` (`corrupt`) rather than
53
+ * silently returning JSON `null`. The directory is created on first write.
47
54
  */
48
55
  export declare class JsonFileStorageAdapter implements StorageAdapter {
49
- private readonly dir;
56
+ private readonly store;
50
57
  constructor(dir: string);
51
- private fileFor;
52
- private ensureDir;
53
58
  get<T>(key: string): Promise<T | null>;
54
59
  set<T>(key: string, value: T): Promise<void>;
55
60
  remove(key: string): Promise<boolean>;
package/dist/signing.js CHANGED
@@ -3,16 +3,16 @@
3
3
  * Host signing material — derives the channel signer, WOTS lease provider,
4
4
  * and Minima address from env-provisioned key material.
5
5
  *
6
- * Key model (matches @totemsdk/omnia's verification path):
6
+ * Key model (RFC-037 unified WOTS):
7
7
  * - `baseSeed` comes from OMNIA_HOST_SEED (BIP39 mnemonic or 32-byte hex)
8
8
  * or from an OMNIA_HOST_KEYFILE JSON.
9
- * - `perAddressSeed = derivePerAddressSeed(baseSeed, localAddressIndex)`.
10
- * - The channel participant's `publicKeyDigest` is the flat WOTS PK digest
11
- * `derivePKdigest(perAddressSeed, 0)` — omnia verifies signatures with
12
- * `wotsVerifyDigest(sig, digest, pkDigest)`, which is key-index agnostic,
13
- * so the signer always signs with key index 0 of the per-address seed.
14
- * - The lease indices {addressIndex, l1, l2} are slot bookkeeping for
15
- * double-sign prevention (watermark monotonicity), not key derivation.
9
+ * - The channel key is the unified child TreeKey
10
+ * `createUnifiedChildTreeKey(baseSeed, localAddressIndex)`; the participant's
11
+ * `publicKeyDigest` is its tree-root public key — the same key the on-chain
12
+ * `MULTISIG` eltoo script embeds.
13
+ * - Signatures are serialized hierarchical `TreeSignature`s (`serializeTreeSignature`)
14
+ * over the reserved leaf `(l1*64 + l2)`, verified by `verifyChannelSignature`.
15
+ * - The lease indices {addressIndex, l1, l2} select the one-time leaf.
16
16
  *
17
17
  * The lease journal/watermark persist in a directory next to the channel DB
18
18
  * (`<dbPath>.lease/`) so they survive restarts together with the channel store.
@@ -30,6 +30,7 @@ const node_fs_1 = __importDefault(require("node:fs"));
30
30
  const node_path_1 = __importDefault(require("node:path"));
31
31
  const node_crypto_1 = require("node:crypto");
32
32
  const core_1 = require("@totemsdk/core");
33
+ const fs_1 = require("@totemsdk/storage/fs");
33
34
  const wots_lease_1 = require("@totemsdk/wots-lease");
34
35
  const wots_lease_2 = require("@totemsdk/wots-lease");
35
36
  const KEYFILE_SCRYPT_N = 16384;
@@ -115,58 +116,34 @@ function resolveBaseSeed(config) {
115
116
  }
116
117
  /**
117
118
  * JSON-file-backed StorageAdapter rooted at a directory next to the channel DB.
118
- * Each key is stored as `<key>.json`; the directory is created on first write.
119
+ *
120
+ * Delegates to the hardened shared `FileStore` from `@totemsdk/storage/fs`
121
+ * (RFC-007): one versioned codec record per key written temp+rename with an
122
+ * fsync; corrupt records surface as `StorageError` (`corrupt`) rather than
123
+ * silently returning JSON `null`. The directory is created on first write.
119
124
  */
120
125
  class JsonFileStorageAdapter {
121
- dir;
126
+ store;
122
127
  constructor(dir) {
123
- this.dir = dir;
124
- }
125
- fileFor(key) {
126
- const safe = key.replace(/[^a-zA-Z0-9_-]/g, '_');
127
- return node_path_1.default.join(this.dir, `${safe}.json`);
128
- }
129
- ensureDir() {
130
- node_fs_1.default.mkdirSync(this.dir, { recursive: true });
128
+ this.store = new fs_1.FileStore(dir);
131
129
  }
132
130
  async get(key) {
133
- const file = this.fileFor(key);
134
- if (!node_fs_1.default.existsSync(file))
135
- return null;
136
- try {
137
- return JSON.parse(node_fs_1.default.readFileSync(file, 'utf8'));
138
- }
139
- catch {
140
- return null;
141
- }
131
+ return this.store.get(key);
142
132
  }
143
133
  async set(key, value) {
144
- this.ensureDir();
145
- node_fs_1.default.writeFileSync(this.fileFor(key), JSON.stringify(value));
134
+ return this.store.set(key, value);
146
135
  }
147
136
  async remove(key) {
148
- const file = this.fileFor(key);
149
- if (!node_fs_1.default.existsSync(file))
150
- return false;
151
- node_fs_1.default.unlinkSync(file);
152
- return true;
137
+ return this.store.remove(key);
153
138
  }
154
139
  async clear() {
155
- if (!node_fs_1.default.existsSync(this.dir))
156
- return;
157
- for (const entry of node_fs_1.default.readdirSync(this.dir)) {
158
- node_fs_1.default.unlinkSync(node_path_1.default.join(this.dir, entry));
159
- }
140
+ return this.store.clear();
160
141
  }
161
142
  async keys() {
162
- if (!node_fs_1.default.existsSync(this.dir))
163
- return [];
164
- return node_fs_1.default.readdirSync(this.dir)
165
- .filter((name) => name.endsWith('.json'))
166
- .map((name) => name.slice(0, -'.json'.length));
143
+ return this.store.keys();
167
144
  }
168
145
  async has(key) {
169
- return node_fs_1.default.existsSync(this.fileFor(key));
146
+ return this.store.has(key);
170
147
  }
171
148
  }
172
149
  exports.JsonFileStorageAdapter = JsonFileStorageAdapter;
@@ -196,25 +173,34 @@ function createHostSigning(config) {
196
173
  throw new Error(`localAddressIndex ${addressIndex} is outside device slot ${deviceSlot} range ` +
197
174
  `${range.startAddressIndex}–${range.endAddressIndex}`);
198
175
  }
199
- const perAddressSeed = (0, core_1.derivePerAddressSeed)(baseSeed, addressIndex);
200
- const kp = (0, core_1.wotsKeypairFromSeed)(perAddressSeed, 0);
201
- const publicKeyDigest = `0x${(0, core_1.bytesToHex)(kp.pk)}`;
202
- const address = (0, core_1.wotsAddressFromKeypair)(kp);
176
+ const treeKey = (0, core_1.createUnifiedChildTreeKey)(baseSeed, addressIndex);
177
+ const publicKey = treeKey.getPublicKey();
178
+ const publicKeyDigest = `0x${(0, core_1.bytesToHex)(publicKey)}`;
179
+ const address = (0, core_1.scriptToAddress)((0, core_1.scriptFromWotsPk)(publicKey));
180
+ const storage = new JsonFileStorageAdapter(leaseStorageDir(config.dbPath));
181
+ const leaseProvider = new wots_lease_1.LocalLeaseProvider(storage, undefined, deviceId);
203
182
  const signer = {
204
183
  publicKeyDigest,
205
- async sign(payload, _indices) {
206
- return (0, core_1.wotsSign)(perAddressSeed, 0, payload);
184
+ async sign(payload, indices) {
185
+ // RFC-020 C1: honor the reserved leaf instead of hardcoding index 0. The
186
+ // reservation must belong to this signer's address; the leaf within the
187
+ // per-address tree is (l1*64 + l2). Signatures are serialized TreeSignatures
188
+ // (RFC-037), verified by verifyChannelSignature against the tree-root pubkey.
189
+ if (indices.addressIndex !== addressIndex) {
190
+ throw new Error(`omnia-host signer address ${addressIndex} cannot sign reserved address ${indices.addressIndex}`);
191
+ }
192
+ const leafIndex = indices.l1 * 64 + indices.l2;
193
+ treeKey.setUses(leafIndex);
194
+ return (0, core_1.serializeTreeSignature)(treeKey.sign(payload));
207
195
  },
208
196
  };
209
- const storage = new JsonFileStorageAdapter(leaseStorageDir(config.dbPath));
210
- const leaseProvider = new wots_lease_1.LocalLeaseProvider(storage, undefined, deviceId);
211
197
  return {
212
198
  signer,
213
199
  leaseProvider,
214
200
  address,
215
201
  publicKeyDigest,
216
202
  baseSeed,
217
- perAddressSeed,
203
+ treeKey,
218
204
  addressIndex,
219
205
  deviceId,
220
206
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@totemsdk/omnia-host",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
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,17 +22,18 @@
22
22
  "dependencies": {
23
23
  "better-sqlite3": "^12.2.0",
24
24
  "ws": "^8.21.1",
25
- "@totemsdk/core": "^1.2.9",
26
- "@totemsdk/identity": "^1.0.0",
25
+ "@totemsdk/chain-provider": "^0.2.1",
26
+ "@totemsdk/identity": "^1.0.1",
27
+ "@totemsdk/core": "^1.2.11",
28
+ "@totemsdk/omnia": "^2.0.0",
27
29
  "@totemsdk/manifest": "^1.0.0",
28
- "@totemsdk/chain-provider": "^0.2.0",
29
- "@totemsdk/omnia": "^1.0.1",
30
- "@totemsdk/omnia-factory": "^0.2.0",
31
- "@totemsdk/omnia-router": "^0.2.0",
32
- "@totemsdk/omnia-splice": "^0.2.0",
33
- "@totemsdk/minima-rpc": "^0.2.0",
34
- "@totemsdk/omnia-vtxo": "^0.2.0",
35
- "@totemsdk/wots-lease": "^1.0.0"
30
+ "@totemsdk/omnia-factory": "^0.3.0",
31
+ "@totemsdk/omnia-router": "^0.3.0",
32
+ "@totemsdk/omnia-vtxo": "^0.4.0",
33
+ "@totemsdk/minima-rpc": "^0.2.2",
34
+ "@totemsdk/omnia-splice": "^0.3.0",
35
+ "@totemsdk/storage": "^0.1.0",
36
+ "@totemsdk/wots-lease": "^1.0.1"
36
37
  },
37
38
  "devDependencies": {
38
39
  "@types/jest": "^30.0.0",
@@ -42,7 +43,7 @@
42
43
  "jest": "^30.4.2",
43
44
  "ts-jest": "^29.4.11",
44
45
  "typescript": "5.9.3",
45
- "@totemsdk/txpow": "^1.0.0"
46
+ "@totemsdk/txpow": "^1.0.2"
46
47
  },
47
48
  "engines": {
48
49
  "node": ">=20.0.0"