@xkova/sdk 0.1.0-alpha.6 → 0.1.0-alpha.8

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.
@@ -0,0 +1,116 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
3
+
4
+ /** Install pg separately. The application owns the pool, schema, retention and key rotation. */
5
+ export class PostgresCheckpointStore {
6
+ #context = new AsyncLocalStorage();
7
+ #key;
8
+
9
+ constructor(pool, namespace, encryptionKey) {
10
+ if (!/^[a-z][a-z0-9_-]{0,63}$/.test(namespace)) throw new Error('Invalid store namespace');
11
+ if (!Buffer.isBuffer(encryptionKey) || encryptionKey.length !== 32)
12
+ throw new Error('A 32-byte application encryption key is required');
13
+ this.pool = pool;
14
+ this.namespace = namespace;
15
+ this.#key = Buffer.from(encryptionKey);
16
+ }
17
+
18
+ #binding(reference) {
19
+ return JSON.stringify([this.namespace, reference]);
20
+ }
21
+
22
+ #encrypt(reference, record) {
23
+ const iv = randomBytes(12);
24
+ const cipher = createCipheriv('aes-256-gcm', this.#key, iv);
25
+ cipher.setAAD(Buffer.from(this.#binding(reference)));
26
+ const bytes = Buffer.concat([cipher.update(JSON.stringify(record), 'utf8'), cipher.final()]);
27
+ return Buffer.concat([iv, cipher.getAuthTag(), bytes]);
28
+ }
29
+
30
+ #decrypt(reference, bytes) {
31
+ if (!Buffer.isBuffer(bytes) || bytes.length < 29) throw new Error('Invalid stored envelope');
32
+ const decipher = createDecipheriv('aes-256-gcm', this.#key, bytes.subarray(0, 12));
33
+ decipher.setAAD(Buffer.from(this.#binding(reference)));
34
+ decipher.setAuthTag(bytes.subarray(12, 28));
35
+ return JSON.parse(
36
+ Buffer.concat([decipher.update(bytes.subarray(28)), decipher.final()]).toString('utf8'),
37
+ );
38
+ }
39
+
40
+ #db() {
41
+ return this.#context.getStore()?.client ?? this.pool;
42
+ }
43
+
44
+ async put(reference, record) {
45
+ await this.#db().query(
46
+ 'INSERT INTO xkova_helper_checkpoints(namespace, reference, envelope, expires_at) VALUES ($1,$2,$3,$4)',
47
+ [this.namespace, reference, this.#encrypt(reference, record), this.#expiry(record)],
48
+ );
49
+ }
50
+
51
+ async get(reference) {
52
+ const result = await this.#db().query(
53
+ 'SELECT envelope FROM xkova_helper_checkpoints WHERE namespace=$1 AND reference=$2 AND expires_at > now()',
54
+ [this.namespace, reference],
55
+ );
56
+ return result.rowCount ? this.#decrypt(reference, result.rows[0].envelope) : null;
57
+ }
58
+
59
+ async replace(reference, record) {
60
+ const result = await this.#db().query(
61
+ 'UPDATE xkova_helper_checkpoints SET envelope=$3, expires_at=$4 WHERE namespace=$1 AND reference=$2',
62
+ [this.namespace, reference, this.#encrypt(reference, record), this.#expiry(record)],
63
+ );
64
+ if (result.rowCount !== 1) throw new Error('Cannot replace a missing checkpoint');
65
+ }
66
+
67
+ async delete(reference) {
68
+ await this.#db().query(
69
+ 'DELETE FROM xkova_helper_checkpoints WHERE namespace=$1 AND reference=$2',
70
+ [this.namespace, reference],
71
+ );
72
+ }
73
+
74
+ #expiry(record) {
75
+ const value = record.applicationExpiresAt ?? record.expiresAt;
76
+ if (!Number.isSafeInteger(value)) throw new Error('Invalid checkpoint expiry');
77
+ return new Date(value);
78
+ }
79
+
80
+ /** A session lock, with separate autocommit checkpoints, never an outer SQL transaction. */
81
+ async runExclusive(reference, work) {
82
+ const existing = this.#context.getStore();
83
+ if (existing) {
84
+ if (existing.reference !== reference) throw new Error('Nested references are unsupported');
85
+ return work();
86
+ }
87
+ const client = await this.pool.connect();
88
+ let locked = false;
89
+ let discard = false;
90
+ try {
91
+ await client.query("SET lock_timeout = '5s'");
92
+ await client.query("SET statement_timeout = '10s'");
93
+ await client.query('SELECT pg_advisory_lock(hashtextextended($1, 0))', [
94
+ this.#binding(reference),
95
+ ]);
96
+ locked = true;
97
+ return await this.#context.run({ client, reference }, work);
98
+ } finally {
99
+ try {
100
+ if (locked) {
101
+ const result = await client.query(
102
+ 'SELECT pg_advisory_unlock(hashtextextended($1, 0)) AS released',
103
+ [this.#binding(reference)],
104
+ );
105
+ if (result.rows[0]?.released !== true) throw new Error('Checkpoint lock was lost');
106
+ }
107
+ await client.query('RESET lock_timeout');
108
+ await client.query('RESET statement_timeout');
109
+ } catch {
110
+ // A connection with an uncertain lock must never return to the pool.
111
+ discard = true;
112
+ }
113
+ client.release(discard);
114
+ }
115
+ }
116
+ }
@@ -0,0 +1,9 @@
1
+ -- Apply through your application's normal migration process, to its own database.
2
+ CREATE TABLE xkova_helper_checkpoints (
3
+ namespace text NOT NULL,
4
+ reference text NOT NULL,
5
+ envelope bytea NOT NULL,
6
+ expires_at timestamptz NOT NULL,
7
+ PRIMARY KEY (namespace, reference)
8
+ );
9
+ CREATE INDEX xkova_helper_checkpoints_expiry ON xkova_helper_checkpoints (expires_at);
package/llms.txt CHANGED
@@ -9,6 +9,7 @@ Use this package on a Node.js 22+ server. Read the installed declarations before
9
9
  - `dist/generated/types.d.ts`: exact request bodies, responses, parameters, and operation IDs. Import types through @xkova/sdk/types.
10
10
  - `dist/member-bff.d.ts`: authorization, session persistence, refresh, logout, action reviews, and human authorization.
11
11
  - `dist/member-resources.d.ts`: typed resources bound to the exact authenticated member.
12
+ - `dist/workforce-bff.d.ts`: confidential workforce sessions, durable callback recovery, exact action reviews and fresh provider proof. Use @xkova/sdk/workforce-bff, separately from member login and Console operators.
12
13
  - [Quickstart](https://docs.xkova.com/getting-started.html): sandbox access and first authenticated call.
13
14
  - [Customer OpenAPI](https://github.com/XKOVA/typescript-sdk/blob/main/schema/customer-api.json): machine-readable contract for the public SDK source. For an installed release, use its generated declarations.
14
15
  - [SDK guide](https://docs.xkova.com/sdk-overview.html): client and resource examples.
@@ -22,6 +23,7 @@ Use this package on a Node.js 22+ server. Read the installed declarations before
22
23
  1. Server business integration: create XKOVAClient.forTenant({ baseUrl, apiKey }). Use https://sandbox-api.xkova.com for sandbox and https://api.xkova.com for production. The API key binds the workspace and mode.
23
24
  2. Member application: use XKOVAMemberBffClient from @xkova/sdk/member-bff in your BFF. Resolve confidential sessions to request-scoped resources with resolveResources(). The browser sends only its application cookie and CSRF proof to your BFF.
24
25
  3. Browser companion: optionally install @xkova/browser. It calls your same-origin routes; it does not create those routes or install a session store.
26
+ 4. Customer staff application: use XKOVAWorkforceBffClient from @xkova/sdk/workforce-bff, an explicitly registered dedicated principal and immutable admitted provider subjects. Resolve each authenticated person per request. API-key attribution, email, groups and member credentials cannot create staff authority.
25
27
 
26
28
  ## Find a capability
27
29
 
@@ -40,6 +42,7 @@ Each namespace below is a property of XKOVAClient. Read its declaration under di
40
42
  | Transaction history | transactions |
41
43
  | Fee schedules and platform fees | feeSchedules |
42
44
  | Customer tokens, deployment, minting, supply, access | tokenization |
45
+ | Tenant treasury vaults, proposals, owner signatures, quorum and transfer status | treasury |
43
46
  | Real-world assets, cap tables, distributions, corporate actions, provenance | rwa |
44
47
  | Gateway registration and conformance | integrationGateways |
45
48
  | Branding and sending-domain configuration | branding |
@@ -48,6 +51,7 @@ Each namespace below is a property of XKOVAClient. Read its declaration under di
48
51
  | Webhook subscriptions and delivery operations | webhooks |
49
52
  | Token deployment/mint polling and off-ramp initiation | workflows |
50
53
  | Session exchange, durable review recovery, fresh human authorization | @xkova/sdk/member-bff |
54
+ | Institution workforce login, scoped sessions and exact action proofs | @xkova/sdk/workforce-bff |
51
55
  | Verify webhook signatures over raw body bytes | @xkova/sdk/webhook |
52
56
  | Offline RWA transparency proofs | @xkova/sdk/rwa-verify |
53
57
  | Customer contract ABIs | @xkova/sdk/abi |
@@ -60,6 +64,7 @@ API availability does not grant permission. Product entitlement, selected API-ke
60
64
  - Use exported package entry points and typed resource methods. Do not import dist files as runtime subpaths, fabricate request fields, or suppress type errors with any.
61
65
  - Production writes require the bearer key plus an active asymmetric requestSigner credential. Its async callback signs through your key provider. The SDK supplies RFC 9421 signatures and RFC 9530 digests. Production reads use bearer authentication.
62
66
  - Generate one idempotency key before a logical mutation and persist it. Reuse it only with the identical request. The SDK does not automatically retry writes. A timeout or disconnect can leave the outcome unknown; reconcile the existing resource before starting another operation.
67
+ - Treasury methods preserve API roles, governance holds, independent owner signatures and Safe quorum. An API key is not an authenticated staff session or a wallet signer. Use isHeldApproval before reading an entity from a governed mutation; approval does not itself produce a signature or prove settlement.
63
68
  - Use decimal strings for monetary values according to the request type. Preserve asset and chain identity throughout a flow. Do not perform monetary calculations with JavaScript floating-point numbers.
64
69
  - Read pagination and response types. Use paginateCursor or collectCursor where applicable; bound collection sizes and polling. Propagate AbortSignal and request deadlines.
65
70
  - Handle XKOVAApiError with its code, status, correlationId, and retryAfter. Handle XKOVAUploadError for the object-storage stage. Do not expose transport headers, provider proofs, or signed URLs in user errors or logs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xkova/sdk",
3
- "version": "0.1.0-alpha.6",
3
+ "version": "0.1.0-alpha.8",
4
4
  "description": "TypeScript SDK for the XKOVA API.",
5
5
  "homepage": "https://docs.xkova.com",
6
6
  "repository": {
@@ -31,6 +31,10 @@
31
31
  "types": "./dist/member-bff.d.ts",
32
32
  "default": "./dist/member-bff.js"
33
33
  },
34
+ "./workforce-bff": {
35
+ "types": "./dist/workforce-bff.d.ts",
36
+ "default": "./dist/workforce-bff.js"
37
+ },
34
38
  "./webhook": {
35
39
  "types": "./dist/webhook.d.ts",
36
40
  "default": "./dist/webhook.js"
@@ -46,7 +50,8 @@
46
50
  },
47
51
  "files": [
48
52
  "dist",
49
- "llms.txt"
53
+ "llms.txt",
54
+ "examples"
50
55
  ],
51
56
  "dependencies": {
52
57
  "viem": "^2.21.0"