@xkova/sdk 0.1.0-alpha.7 → 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.
- package/README.md +105 -0
- package/dist/client.d.ts +4 -0
- package/dist/client.js +59 -1
- package/dist/generated/types.d.ts +7515 -4653
- package/dist/held-approval.d.ts +41 -0
- package/dist/held-approval.js +33 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/request-signing.js +6 -0
- package/dist/resources/treasury.d.ts +404 -0
- package/dist/resources/treasury.js +115 -0
- package/dist/resources/workforce-wallets.d.ts +34 -0
- package/dist/resources/workforce-wallets.js +19 -0
- package/dist/workforce-bff.d.ts +202 -0
- package/dist/workforce-bff.js +462 -0
- package/llms.txt +5 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -93,6 +93,77 @@ one live successor; either key can be revoked immediately.
|
|
|
93
93
|
|
|
94
94
|
## Typed resources
|
|
95
95
|
|
|
96
|
+
### Customer workforce applications
|
|
97
|
+
|
|
98
|
+
Use `@xkova/sdk/workforce-bff` on a confidential server for an institution's
|
|
99
|
+
staff application. It is separate from member login and Console operator
|
|
100
|
+
sessions. The Console administrator first registers an exact issuer, audience,
|
|
101
|
+
JWKS endpoint, application origins/callbacks and dedicated workspace API
|
|
102
|
+
principal. They explicitly admit each immutable provider subject to an existing
|
|
103
|
+
staff membership with narrowed roles and assigned resources. Email and provider
|
|
104
|
+
groups never grant authority. A transport key cannot admit its own users.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { XKOVAWorkforceBffClient } from '@xkova/sdk/workforce-bff';
|
|
108
|
+
|
|
109
|
+
const workforce = new XKOVAWorkforceBffClient({
|
|
110
|
+
baseUrl: 'https://sandbox-api.xkova.com',
|
|
111
|
+
apiKey: process.env.XKOVA_WORKFORCE_API_KEY!,
|
|
112
|
+
workforceKey: process.env.XKOVA_WORKFORCE_KEY!,
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Use separate encrypted durable transaction/session stores implementing
|
|
117
|
+
`XKOVAWorkforceStore`. Each write must commit independently of a later callback
|
|
118
|
+
error. Serialize each reference across replicas, and make `replace` fail when
|
|
119
|
+
the record is absent. The included PostgreSQL adapter demonstrates this contract.
|
|
120
|
+
Use a distinct namespace from member sessions. Keep session material, provider
|
|
121
|
+
proofs, PKCE verifiers and refresh attempts on the server.
|
|
122
|
+
|
|
123
|
+
`beginAuthorization` returns the registered provider endpoint, audience, nonce,
|
|
124
|
+
PKCE challenge and policy. Use a short-lived HttpOnly flow cookie for its state.
|
|
125
|
+
Your BFF exchanges the real provider code and verifies its signature, issuer,
|
|
126
|
+
audience, nonce and PKCE through `completeAuthorizationCallback`. XKOVA performs
|
|
127
|
+
its own registered-provider verification before issuing the workforce session.
|
|
128
|
+
An uncertain provider exchange cannot redeem the code again. A staged proof can
|
|
129
|
+
recover the same API exchange attempt. A provider token's actual expiry bounds
|
|
130
|
+
the session; refreshing XKOVA credentials does not refresh upstream identity.
|
|
131
|
+
|
|
132
|
+
Read only `projectXKOVAWorkforceSession` into browser UI. Resolve resource access
|
|
133
|
+
per request with `resolveResources({ reference, sessionStore, browserOrigin })`.
|
|
134
|
+
The browser's Origin and session-bound CSRF proof must be checked before writes.
|
|
135
|
+
The BFF pins its registered origin, workforce application and resolved human
|
|
136
|
+
session. Roles/scopes/resources are intersected by the API, never combined with
|
|
137
|
+
the transport principal's authority. A member credential cannot supply workforce
|
|
138
|
+
authority, and workforce sessions cannot enter Console operator routes.
|
|
139
|
+
|
|
140
|
+
For `human_authorization_required`, preserve the exact request, body, origin and
|
|
141
|
+
idempotency key in a private review record. Use `inspectAction` and
|
|
142
|
+
`beginActionProof` for its exact `authorization_token`. Show the returned review
|
|
143
|
+
before starting real fresh provider authentication with the returned nonce and
|
|
144
|
+
qualified assurance policy. Validate the provider callback's own state, PKCE,
|
|
145
|
+
issuer, audience, nonce and current subject; require fresh authentication rather
|
|
146
|
+
than accepting a cached login. Submit the real token with `completeActionProof`.
|
|
147
|
+
Keep its short-lived grant server-side and pass it as `actionGrant` to
|
|
148
|
+
`resolveResources` for only the unchanged frozen request. A grant never approves
|
|
149
|
+
another request or replaces independent Safe signatures/quorum. These methods
|
|
150
|
+
do not automatically retry an action or an uncertain proof completion.
|
|
151
|
+
|
|
152
|
+
`logoutSession` durably blocks the local session before retiring it at XKOVA.
|
|
153
|
+
Retry only that same logout if the network result is uncertain. It does not
|
|
154
|
+
promise a global logout at the external provider. No request may fall back to a
|
|
155
|
+
machine principal after workforce authentication fails.
|
|
156
|
+
|
|
157
|
+
The resolved `wallets` resource supports `list`, `provision`, `register` and
|
|
158
|
+
`signingBootstrap`. Provisioning and signing need the exact one-use human grant.
|
|
159
|
+
Register only after signing the API's returned enrollment typed data. Signing
|
|
160
|
+
bootstrap requires the existing proposal ID, its frozen Safe digest and nonce.
|
|
161
|
+
Use `@xkova/browser/wallet` for the isolated browser ceremony, with a fresh
|
|
162
|
+
purpose-bound admission and the expected wallet address. Keep the BFF key,
|
|
163
|
+
provider proofs and workforce session credentials on the server. Provider
|
|
164
|
+
admission does not replace wallet possession, current signer eligibility,
|
|
165
|
+
independent-person quorum or confirmed chain settlement.
|
|
166
|
+
|
|
96
167
|
Call resource methods with typed inputs:
|
|
97
168
|
|
|
98
169
|
```ts
|
|
@@ -533,6 +604,40 @@ try {
|
|
|
533
604
|
}
|
|
534
605
|
```
|
|
535
606
|
|
|
607
|
+
## Treasury vaults and transfer proposals
|
|
608
|
+
|
|
609
|
+
Use `client.treasury` from a trusted backend with the workspace's explicitly
|
|
610
|
+
granted treasury scopes and roles. Discover the chain and eligible assets before
|
|
611
|
+
creating a vault or proposal. An existing same-chain vault retains its real
|
|
612
|
+
owner set and threshold.
|
|
613
|
+
|
|
614
|
+
```ts
|
|
615
|
+
import { isHeldApproval } from '@xkova/sdk';
|
|
616
|
+
|
|
617
|
+
const vaults = await xkova.treasury.listWallets();
|
|
618
|
+
const proposal = await xkova.treasury.getTransfer('trf_...');
|
|
619
|
+
const counterparties = await xkova.treasury.listCounterparties({ limit: 20 });
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
Vault and counterparty setup can return a governance hold. Check `isHeldApproval`
|
|
623
|
+
before reading entity fields. Keep the original request and idempotency key;
|
|
624
|
+
resume through normal policy after independently eligible approvers complete the
|
|
625
|
+
required approval. The SDK does not approve, sign or retry a held operation.
|
|
626
|
+
|
|
627
|
+
`draftTransfer` creates a proposal. `getTransfer` reads its frozen signing context
|
|
628
|
+
and current quorum. Each eligible owner produces their own signature over that
|
|
629
|
+
exact Safe transaction. `submitTransferSignature` submits an existing signature;
|
|
630
|
+
it does not create a wallet or sign on anyone's behalf. `executeTransfer` retains
|
|
631
|
+
current role, signer, nonce, quorum and policy checks. A returned relay result is
|
|
632
|
+
not settlement; read the proposal until its actual confirmed or failed outcome.
|
|
633
|
+
After a timeout, reconcile the existing proposal before creating another one.
|
|
634
|
+
|
|
635
|
+
The API key's attributed staff ID is audit metadata, not a per-human session or
|
|
636
|
+
permission to bootstrap a staff wallet. This namespace does not expose Console
|
|
637
|
+
credentials, staff-wallet bootstrap credentials, or raw private-key material.
|
|
638
|
+
The supported workforce authentication and independent signer ceremonies remain
|
|
639
|
+
separate requirements for a complete customer treasury application.
|
|
640
|
+
|
|
536
641
|
## Webhooks and generated types
|
|
537
642
|
|
|
538
643
|
```ts
|
package/dist/client.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { RampsResource } from './resources/ramps.js';
|
|
|
13
13
|
import { RwaResource } from './resources/rwa.js';
|
|
14
14
|
import { TenantResource } from './resources/tenant.js';
|
|
15
15
|
import { TokenizationResource } from './resources/tokenization.js';
|
|
16
|
+
import { TreasuryResource } from './resources/treasury.js';
|
|
16
17
|
import { TransactionsResource } from './resources/transactions.js';
|
|
17
18
|
import { WalletsResource } from './resources/wallets.js';
|
|
18
19
|
import { WebhooksResource } from './resources/webhooks.js';
|
|
@@ -47,6 +48,7 @@ export interface XKOVAMutationOptions extends XKOVARequestControl {
|
|
|
47
48
|
export declare class XKOVAClient {
|
|
48
49
|
private readonly config;
|
|
49
50
|
private readonly memberContext?;
|
|
51
|
+
private readonly workforceContext?;
|
|
50
52
|
private readonly apiBaseUrl;
|
|
51
53
|
private readonly fetchImpl;
|
|
52
54
|
private constructor();
|
|
@@ -65,6 +67,8 @@ export declare class XKOVAClient {
|
|
|
65
67
|
get rwa(): RwaResource;
|
|
66
68
|
get tenant(): TenantResource;
|
|
67
69
|
get tokenization(): TokenizationResource;
|
|
70
|
+
/** Tenant treasury operations retain server-side roles, governance and signer checks. */
|
|
71
|
+
get treasury(): TreasuryResource;
|
|
68
72
|
get transactions(): TransactionsResource;
|
|
69
73
|
get wallets(): WalletsResource;
|
|
70
74
|
get webhooks(): WebhooksResource;
|
package/dist/client.js
CHANGED
|
@@ -16,6 +16,7 @@ import { RampsResource } from './resources/ramps.js';
|
|
|
16
16
|
import { RwaResource } from './resources/rwa.js';
|
|
17
17
|
import { TenantResource } from './resources/tenant.js';
|
|
18
18
|
import { TokenizationResource } from './resources/tokenization.js';
|
|
19
|
+
import { TreasuryResource } from './resources/treasury.js';
|
|
19
20
|
import { TransactionsResource } from './resources/transactions.js';
|
|
20
21
|
import { WalletsResource } from './resources/wallets.js';
|
|
21
22
|
import { WebhooksResource } from './resources/webhooks.js';
|
|
@@ -25,11 +26,13 @@ import { WorkspacesResource } from './resources/workspaces.js';
|
|
|
25
26
|
export class XKOVAClient {
|
|
26
27
|
config;
|
|
27
28
|
memberContext;
|
|
29
|
+
workforceContext;
|
|
28
30
|
apiBaseUrl;
|
|
29
31
|
fetchImpl;
|
|
30
|
-
constructor(config, memberContext) {
|
|
32
|
+
constructor(config, memberContext, workforceContext) {
|
|
31
33
|
this.config = config;
|
|
32
34
|
this.memberContext = memberContext;
|
|
35
|
+
this.workforceContext = workforceContext;
|
|
33
36
|
if (config.timeoutMs !== undefined &&
|
|
34
37
|
(!Number.isFinite(config.timeoutMs) || config.timeoutMs <= 0)) {
|
|
35
38
|
throw new Error('XKOVAClient timeoutMs must be a positive finite number');
|
|
@@ -83,6 +86,10 @@ export class XKOVAClient {
|
|
|
83
86
|
get tokenization() {
|
|
84
87
|
return (this.namespaces.tokenization ??= new TokenizationResource(this));
|
|
85
88
|
}
|
|
89
|
+
/** Tenant treasury operations retain server-side roles, governance and signer checks. */
|
|
90
|
+
get treasury() {
|
|
91
|
+
return (this.namespaces.treasury ??= new TreasuryResource(this));
|
|
92
|
+
}
|
|
86
93
|
get transactions() {
|
|
87
94
|
return (this.namespaces.transactions ??= new TransactionsResource(this));
|
|
88
95
|
}
|
|
@@ -108,6 +115,20 @@ export class XKOVAClient {
|
|
|
108
115
|
static forMemberSession(options, context) {
|
|
109
116
|
return new XKOVAClient(options, Object.freeze({ ...context }));
|
|
110
117
|
}
|
|
118
|
+
/** @internal Created only after the workforce BFF resolves its confidential store. */
|
|
119
|
+
static forWorkforceSession(options, context) {
|
|
120
|
+
if (!/^xkova_wf_[A-Za-z0-9_-]{43}$/.test(context.workforceKey) ||
|
|
121
|
+
!/^xwfs_[A-Za-z0-9_-]{43}$/.test(context.sessionToken) ||
|
|
122
|
+
(context.humanAuthorization !== undefined &&
|
|
123
|
+
!/^[A-Za-z0-9_-]{43}$/.test(context.humanAuthorization))) {
|
|
124
|
+
throw new TypeError('Invalid workforce session material');
|
|
125
|
+
}
|
|
126
|
+
const origin = new URL(context.browserOrigin);
|
|
127
|
+
if (origin.origin !== context.browserOrigin || !isAllowedProtocol(origin)) {
|
|
128
|
+
throw new TypeError('Invalid workforce application origin');
|
|
129
|
+
}
|
|
130
|
+
return new XKOVAClient(options, undefined, Object.freeze({ ...context }));
|
|
131
|
+
}
|
|
111
132
|
/** @internal Resource transport. Use typed namespaces instead. */
|
|
112
133
|
async requestJson(options) {
|
|
113
134
|
const prepared = await this.prepareRequest(options);
|
|
@@ -211,6 +232,9 @@ export class XKOVAClient {
|
|
|
211
232
|
headers.set(name, value);
|
|
212
233
|
}
|
|
213
234
|
if (this.memberContext) {
|
|
235
|
+
if (headers.has('X-Workforce-Session') || headers.has('X-XKOVA-Workforce-Key')) {
|
|
236
|
+
throw new TypeError('Member and workforce authority cannot be combined');
|
|
237
|
+
}
|
|
214
238
|
const authority = {
|
|
215
239
|
Authorization: `Bearer ${this.config.apiKey}`,
|
|
216
240
|
'X-Member-Session-Id': this.memberContext.memberSessionId,
|
|
@@ -224,6 +248,40 @@ export class XKOVAClient {
|
|
|
224
248
|
headers.set(name, value);
|
|
225
249
|
}
|
|
226
250
|
}
|
|
251
|
+
if (this.workforceContext) {
|
|
252
|
+
if (headers.has('X-XKOVA-Human-Authorization') && !this.workforceContext.humanAuthorization) {
|
|
253
|
+
throw new TypeError('A workforce action grant must come from the confidential context');
|
|
254
|
+
}
|
|
255
|
+
for (const name of [
|
|
256
|
+
'Cookie',
|
|
257
|
+
'X-Member-Session-Id',
|
|
258
|
+
'X-XKOVA-Publishable-Key',
|
|
259
|
+
'X-Account-Holder-Id',
|
|
260
|
+
'X-End-User-Id',
|
|
261
|
+
'X-OIDC-ID-Token',
|
|
262
|
+
'X-Active-Scope',
|
|
263
|
+
'X-Workspace-Id',
|
|
264
|
+
'X-Workspace-Type',
|
|
265
|
+
]) {
|
|
266
|
+
if (headers.has(name))
|
|
267
|
+
throw new TypeError('A workforce operation cannot mix identity or workspace contexts');
|
|
268
|
+
}
|
|
269
|
+
const authority = {
|
|
270
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
271
|
+
'X-XKOVA-Workforce-Key': this.workforceContext.workforceKey,
|
|
272
|
+
'X-Workforce-Session': this.workforceContext.sessionToken,
|
|
273
|
+
Origin: this.workforceContext.browserOrigin,
|
|
274
|
+
...(this.workforceContext.humanAuthorization !== undefined && {
|
|
275
|
+
'X-XKOVA-Human-Authorization': this.workforceContext.humanAuthorization,
|
|
276
|
+
}),
|
|
277
|
+
};
|
|
278
|
+
for (const [name, value] of Object.entries(authority)) {
|
|
279
|
+
if (headers.has(name) && headers.get(name) !== value) {
|
|
280
|
+
throw new TypeError('A workforce-bound operation cannot replace its session authority');
|
|
281
|
+
}
|
|
282
|
+
headers.set(name, value);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
227
285
|
return headers;
|
|
228
286
|
}
|
|
229
287
|
requestSignal(signal) {
|