@witnium-tech/witniumchain 0.5.0 → 0.6.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.
@@ -0,0 +1,297 @@
1
+ # WitniumChain — Frontend Integration Guide
2
+
3
+ Audience: the team building the WitniumChain dashboard frontend. This is the
4
+ contract between the backend (accounts + chain-api) and your UI. It covers the
5
+ end-to-end user flows, where keys live, the recovery-file format, and the
6
+ known sharp edges discovered during backend validation.
7
+
8
+ The SDK that backs all of this is **`@witnium-tech/witniumchain`** (npm). Every
9
+ flow below has a typed method on it.
10
+
11
+ ---
12
+
13
+ ## 1. The custody model — read this first
14
+
15
+ Two ed25519 keys are generated **client-side** at signup and never leave the
16
+ user's device in plaintext:
17
+
18
+ | Key | Controls | Used for |
19
+ |---|---|---|
20
+ | **Owner key** | The contract's signing-key set (add/revoke), pause/unpause | Authorizing changes. Never signs witnesses. |
21
+ | **Signing key** | — | Signing witnesses day-to-day |
22
+
23
+ Witnium only ever receives the **public** halves. The backend enforces
24
+ `ownerPublicKey !== initialSigningPublicKey`.
25
+
26
+ **Your job on the frontend:** generate these keys, store them encrypted, let
27
+ the user export/import them, and produce owner-key signatures when the backend
28
+ asks for them (the MCP-CUSTODY ceremony, §6). You are the custody surface. If
29
+ you lose the keys with no recovery file, the account's contract is
30
+ unrecoverable — there is no server-side escrow by design.
31
+
32
+ ### Recommended storage (production)
33
+
34
+ The throwaway test frontend stored keys as plain hex in `localStorage`. **Do
35
+ not ship that.** The intended production design:
36
+
37
+ - Wrap the two private keys with a key derived from a **WebAuthn PRF** assertion
38
+ (passkey). The user authenticates with their passkey to decrypt.
39
+ - Issue **recovery codes** at signup (one-time display) as the fallback when no
40
+ passkey is available (new device, lost authenticator).
41
+ - Persist the wrapped blob in IndexedDB.
42
+
43
+ The backend does not need to know how you wrap the keys — it only sees public
44
+ keys + signatures. But see §7 for the **same-origin requirement**, which is a
45
+ hard constraint on where this storage lives.
46
+
47
+ ---
48
+
49
+ ## 2. Install
50
+
51
+ ```bash
52
+ npm install @witnium-tech/witniumchain
53
+ ```
54
+
55
+ ```ts
56
+ import { WitniumchainClient } from '@witnium-tech/witniumchain';
57
+
58
+ const client = new WitniumchainClient({
59
+ baseUrl: 'https://auth.witniumchain.com', // accounts
60
+ chainBaseUrl: 'https://api.witniumchain.com', // chain-api (read methods)
61
+ });
62
+ ```
63
+
64
+ Generate keys with any ed25519 lib. The backend + SDK speak 64-hex-char
65
+ public keys (no `0x`). Example with `@noble/ed25519`:
66
+
67
+ ```ts
68
+ import * as ed from '@noble/ed25519';
69
+ const priv = ed.utils.randomPrivateKey();
70
+ const pub = await ed.getPublicKeyAsync(priv); // hex-encode both halves
71
+ ```
72
+
73
+ ---
74
+
75
+ ## 3. Signup → org → contract (one call)
76
+
77
+ `createOrg` is the self-serve path. It creates the admin user, the
78
+ organization, the org-admin membership, **and deploys the contract** with the
79
+ client-generated keys — all in one request.
80
+
81
+ ```ts
82
+ const result = await client.createOrg({
83
+ orgName: 'Acme Inc.',
84
+ adminEmail: 'you@example.com',
85
+ adminPassword: '≥12 chars',
86
+ ownerPublicKey: ownerPubHex,
87
+ initialSigningPublicKey: signingPubHex,
88
+ });
89
+ // → { orgId, userId, contractAddress, contractVersion, emailVerifyToken }
90
+ ```
91
+
92
+ Persist the two private keys + `contractAddress` locally (wrapped, per §1) and
93
+ prompt a recovery-file download (§5) before doing anything else.
94
+
95
+ `emailVerifyToken` is also emailed to the admin. It's echoed in the response so
96
+ you can render a "click to verify" link inline if you prefer. **The email is
97
+ the login gate, not the contract-deploy gate** — the contract is already live
98
+ when this returns.
99
+
100
+ ---
101
+
102
+ ## 4. Auth flows
103
+
104
+ ### Email verify
105
+
106
+ ```ts
107
+ await client.verifyEmail(token); // GET /v1/auth/verify?token=…
108
+ ```
109
+
110
+ > ⚠️ This is a **GET**, not POST. (Caught in testing — easy to get wrong.)
111
+
112
+ ### Password login
113
+
114
+ ```ts
115
+ await client.login({ email, password }); // sets wac_session cookie
116
+ ```
117
+
118
+ In a browser the `wac_session` cookie is set automatically (the SDK uses
119
+ `credentials: 'include'`). Server-side callers must capture + replay the cookie.
120
+
121
+ ### Magic-link sign-in
122
+
123
+ `beginOAuthLogin` / passwordless paths exist; see the SDK's `OauthNamespace`
124
+ and the accounts OpenAPI for `/v1/auth/magic/*`.
125
+
126
+ ### MFA (TOTP)
127
+
128
+ ```ts
129
+ const { secret, otpauthUrl } = await client.mfa.totp.enroll(); // render QR from otpauthUrl
130
+ const { recoveryCodes } = await client.mfa.totp.confirm({ code }); // show ONCE
131
+ ```
132
+
133
+ ### OAuth 2.1 + PKCE (for when your dashboard is itself an OAuth client)
134
+
135
+ ```ts
136
+ const { authorizationUrl } = await client.beginOAuthLogin({ … });
137
+ // …redirect, then on callback:
138
+ await client.completeOAuthLogin({ … }); // PKCE verifier from sessionStorage
139
+ ```
140
+
141
+ Access token held in memory; refresh token delivered as an HttpOnly
142
+ `wac_refresh` cookie scoped to `/token` (JS never touches it). The SDK does
143
+ transparent single-flight refresh on 401.
144
+
145
+ ---
146
+
147
+ ## 5. Recovery-file format
148
+
149
+ The throwaway frontend used this shape. **Treat it as a starting point** —
150
+ production should wrap `record` with WebAuthn/recovery-code encryption rather
151
+ than storing cleartext keys.
152
+
153
+ ```json
154
+ {
155
+ "version": 1,
156
+ "type": "witnium-recovery",
157
+ "created": "<ISO 8601>",
158
+ "record": {
159
+ "email": "you@example.com",
160
+ "orgId": "<uuid>",
161
+ "userId": "<uuid>",
162
+ "contractAddress": "0x… (lowercase)",
163
+ "contractVersion": "5.0.0",
164
+ "ownerPublicKey": "<64 hex>",
165
+ "ownerPrivateKey": "<64 hex — ENCRYPT THIS>",
166
+ "signingPublicKey": "<64 hex>",
167
+ "signingPrivateKey": "<64 hex — ENCRYPT THIS>"
168
+ }
169
+ }
170
+ ```
171
+
172
+ Import = restore `record` into local storage. The owner private key must be
173
+ retrievable for the MCP-CUSTODY ceremony (§6).
174
+
175
+ ---
176
+
177
+ ## 6. MCP-CUSTODY consent ceremony
178
+
179
+ When a user connects an AI agent (claude.ai's MCP connector, or a self-hosted
180
+ one) the backend runs a ceremony at OAuth-consent time that mints a **fresh
181
+ ephemeral signing key per connection** and asks the user to sign two owner-key
182
+ operations:
183
+
184
+ 1. `addSigningKey(<ephemeral key>)` — registers it on the contract (submitted now)
185
+ 2. `revokeSigningKey(<same key>)` — pre-signed, stored, fired automatically on disconnect
186
+
187
+ **What the frontend must provide:** the page that collects these two
188
+ signatures is currently *server-rendered by accounts* at
189
+ `/interaction/:uid/sign-keys`. It runs inline JS that reads the owner key from
190
+ `localStorage['witnium.ownerPrivateKey:<contractAddress>']`, signs both
191
+ messages, and form-POSTs them back.
192
+
193
+ You have two choices:
194
+
195
+ - **Let accounts render it** (current behavior). Then your only job is to make
196
+ sure the owner key is in `localStorage` under that exact key, **on the
197
+ accounts origin** (see §7).
198
+ - **Render it yourself** and POST the two signatures to
199
+ `/interaction/:uid/sign-keys` (`addOwnerSignature`, `revokeOwnerSignature`,
200
+ `revokeNonce`). Gives you full UX control. The signed message formats are:
201
+ - add: `{"op":1,"contract":"<addr>","nonce":<ownerNonce+1>,"newKey":"<pub>"}`
202
+ - revoke: `{"op":2,"contract":"<addr>","nonce":<ownerNonce+2>,"key":"<pub>"}`
203
+ (canonical JSON, signed as UTF-8 bytes with the owner key)
204
+
205
+ **Dev cross-origin shortcut (pre-launch):** the accounts-rendered sign-keys
206
+ page now also accepts a **recovery-file upload** (the §5 JSON). So while you're
207
+ building on a different origin (e.g. Lovable), the user can export their
208
+ recovery file from your app and upload it on the accounts sign-keys page — the
209
+ page extracts the owner key for the matching contract. This sidesteps the
210
+ same-origin localStorage limitation (§7) without any postMessage plumbing. It's
211
+ a dev convenience, not the production answer; production should solve §7
212
+ properly.
213
+
214
+ To get refresh tokens (required for the agent to stay connected + for the
215
+ disconnect-on-revoke cascade), the OAuth client **must request
216
+ `prompt=consent`** and include `offline_access` in scope. Without
217
+ `prompt=consent` the AS silently drops `offline_access` and no refresh token is
218
+ issued — this is standard OIDC, not a bug.
219
+
220
+ ---
221
+
222
+ ## 7. Same-origin requirement (architectural — needs a decision)
223
+
224
+ **The dashboard that stores keys and the accounts OIDC interaction pages must
225
+ share an origin**, because `localStorage` is origin-scoped. Discovered in
226
+ testing: keys saved on `test-frontend.witniumchain.com` were invisible to the
227
+ sign-keys page rendered at `auth.witniumchain.com` → the ceremony couldn't find
228
+ the owner key.
229
+
230
+ Pick one:
231
+
232
+ - **(Recommended) Serve the dashboard from `auth.witniumchain.com`** (a path,
233
+ or accounts serving the SPA). Owner key in localStorage is naturally visible
234
+ to the OIDC interaction pages. Simplest, no cross-origin plumbing.
235
+ - **Cross-origin with `postMessage`** — dashboard on its own domain passes the
236
+ owner-key signature to the accounts interaction page via a `postMessage`
237
+ handshake. More moving parts; partially erodes the "keys never leave your
238
+ page" story. Only do this if a separate dashboard domain is a hard
239
+ requirement.
240
+
241
+ This decision blocks the key-storage implementation, so settle it early.
242
+
243
+ ---
244
+
245
+ ## 7b. Dev workflow — reset + re-run
246
+
247
+ Testing signup repeatedly piles up users/orgs/contracts. A **pre-launch-only**
248
+ self-serve reset is available:
249
+
250
+ ```ts
251
+ // Authenticated with the wac_session cookie (must be logged in).
252
+ await fetch('https://auth.witniumchain.com/v1/account', {
253
+ method: 'DELETE',
254
+ credentials: 'include',
255
+ });
256
+ // → { deleted: true, userId, organizationId, vaultKeysPurged, notice }
257
+ ```
258
+
259
+ Deletes the caller's user + org + contracts + delegated keys (Vault keys
260
+ destroyed too), so you can sign up fresh. The on-chain contract is orphaned
261
+ (immutable — fine). Returns **403** unless `WITNIUM_PRE_LAUNCH=true` on the
262
+ server (it is, on the current dev deploy). This endpoint will be removed before
263
+ customer onboarding; don't build product UI on it — it's purely a dev-loop tool.
264
+ (Alternatively: just sign up with a fresh email each time, no cleanup needed.)
265
+
266
+ ---
267
+
268
+ ## 8. Known limitations / sharp edges
269
+
270
+ | Issue | Impact | Status |
271
+ |---|---|---|
272
+ | **Pre-signed revoke nonce drift** | If a user opens several agent connections in a row without disconnecting, older connections' on-chain `revokeSigningKey` fails when finally fired (the pre-signed sig is bound to a stale ownerNonce). The Vault key is still destroyed — Witnium genuinely loses signing ability — but the on-chain key stays listed as active. | Known. On-chain cleanup is best-effort under serial grants. Mitigation TBD (revoke-then-add atomic, or periodic re-sign). |
273
+ | **`prompt=consent` required for refresh tokens** | Omitting it silently drops `offline_access`. | Working as designed (OIDC). Document in your OAuth client. |
274
+ | **Email verify is GET** | POST returns "Cannot POST". | Working as designed. |
275
+ | **Same-origin localStorage** | §7. | Architectural decision required. |
276
+
277
+ ---
278
+
279
+ ## 9. API reference
280
+
281
+ - **accounts OpenAPI**: `https://auth.witniumchain.com/openapi.json` (or the
282
+ committed `specs/accounts.openapi.json` in the SDK repo)
283
+ - **chain-api OpenAPI**: `specs/chain-api.openapi.json` in the SDK repo
284
+ - Every SDK method's request/response type is derived from these specs via
285
+ `openapi-typescript`, so they stay in lockstep. Re-export aliases (e.g.
286
+ `CreateOrgRequest`, `SignupResponse`) are in `src/types.ts`.
287
+
288
+ ## 10. SDK clients at a glance
289
+
290
+ | Client | For | Auth |
291
+ |---|---|---|
292
+ | `WitniumchainClient` | end-users (signup, login, OAuth, MFA, delegated keys, witness reads) | session cookie / access token |
293
+ | `WitniumchainOrgClient` | org admins managing members | org API key |
294
+ | `WitniumchainAdminClient` | sysadmin org lifecycle | admin token |
295
+ | `WitniumchainChainAdminClient` | **service-to-service only** — not for frontend use | admin token / service-principal JWT |
296
+
297
+ The frontend lives almost entirely in `WitniumchainClient`.
package/dist/index.d.mts CHANGED
@@ -915,6 +915,139 @@ interface paths$1 {
915
915
  patch?: never;
916
916
  trace?: never;
917
917
  };
918
+ "/v1/orgs": {
919
+ parameters: {
920
+ query?: never;
921
+ header?: never;
922
+ path?: never;
923
+ cookie?: never;
924
+ };
925
+ get?: never;
926
+ put?: never;
927
+ /**
928
+ * Create an organisation with client-generated keys
929
+ * @description Phase RBAC Thread D — explicit org creation. The caller generates the owner key and the initial signing key client-side; Witnium never holds the private bytes. The response includes the freshly-deployed contract address and a one-time email-verify token.
930
+ */
931
+ post: {
932
+ parameters: {
933
+ query?: never;
934
+ header?: never;
935
+ path?: never;
936
+ cookie?: never;
937
+ };
938
+ requestBody?: {
939
+ content: {
940
+ "application/json": {
941
+ /** @description Display name of the organisation. */
942
+ orgName: string;
943
+ /**
944
+ * Format: email
945
+ * @description Email of the founding org-admin. Receives the email-verify link.
946
+ * @example user@example.com
947
+ */
948
+ adminEmail: string;
949
+ /** @description Password for the founding org-admin. argon2id-hashed server-side. Minimum 12 chars; callers are expected to gate via a strength meter. */
950
+ adminPassword: string;
951
+ /**
952
+ * @description Client-generated Ed25519 public key that controls signing-key add/revoke on the new contract. Witnium never sees the private bytes (non-custodial).
953
+ * @example 4cdde7a3e3a1d8a48d2b9eaf2bcbe2db1d57d3e1a8d6c20e0a86c0f9c4b6cf2e
954
+ */
955
+ ownerPublicKey: string;
956
+ /**
957
+ * @description First signing key registered on the contract. Often the same identity as the org-admin's browser-held key (Mode 1) but may also be the public half of a separately-registered Vault-Transit delegated key (Mode 3).
958
+ * @example 4cdde7a3e3a1d8a48d2b9eaf2bcbe2db1d57d3e1a8d6c20e0a86c0f9c4b6cf2e
959
+ */
960
+ initialSigningPublicKey: string;
961
+ /** @description Captcha proof. Not yet wired (no captcha provider configured); reserved for when one is added. Public endpoint without it relies on downstream billing/credit-ledger gates against abuse. */
962
+ captchaToken?: string;
963
+ };
964
+ };
965
+ };
966
+ responses: {
967
+ /** @description Success */
968
+ 200: {
969
+ headers: {
970
+ [name: string]: unknown;
971
+ };
972
+ content: {
973
+ "application/json": {
974
+ /**
975
+ * @description UUID v4
976
+ * @example 550e8400-e29b-41d4-a716-446655440000
977
+ */
978
+ orgId: string;
979
+ /**
980
+ * @description UUID v4
981
+ * @example 550e8400-e29b-41d4-a716-446655440000
982
+ */
983
+ userId: string;
984
+ /** @description Address of the freshly-deployed contract. */
985
+ contractAddress: string;
986
+ /** @description Semantic on-chain Solidity version (e.g. "5.0.0"). */
987
+ contractVersion: string;
988
+ /** @description Email-verify token. Already sent to adminEmail; echoed here for flows that prefer to surface a "click this link" in their own UI. */
989
+ emailVerifyToken: string;
990
+ };
991
+ };
992
+ };
993
+ /** @description Bad Request — validation failed */
994
+ 400: {
995
+ headers: {
996
+ [name: string]: unknown;
997
+ };
998
+ content: {
999
+ "application/json": {
1000
+ /**
1001
+ * @description HTTP status code
1002
+ * @example 400
1003
+ */
1004
+ statusCode?: number;
1005
+ /**
1006
+ * @description Short error label
1007
+ * @example Bad Request
1008
+ */
1009
+ error?: string;
1010
+ /**
1011
+ * @description Human-readable error message. Validation errors from `ZodError.flatten()` may be returned as an array.
1012
+ * @example Validation failed
1013
+ */
1014
+ message: string | string[];
1015
+ };
1016
+ };
1017
+ };
1018
+ /** @description Conflict */
1019
+ 409: {
1020
+ headers: {
1021
+ [name: string]: unknown;
1022
+ };
1023
+ content: {
1024
+ "application/json": {
1025
+ /**
1026
+ * @description HTTP status code
1027
+ * @example 400
1028
+ */
1029
+ statusCode?: number;
1030
+ /**
1031
+ * @description Short error label
1032
+ * @example Bad Request
1033
+ */
1034
+ error?: string;
1035
+ /**
1036
+ * @description Human-readable error message. Validation errors from `ZodError.flatten()` may be returned as an array.
1037
+ * @example Validation failed
1038
+ */
1039
+ message: string | string[];
1040
+ };
1041
+ };
1042
+ };
1043
+ };
1044
+ };
1045
+ delete?: never;
1046
+ options?: never;
1047
+ head?: never;
1048
+ patch?: never;
1049
+ trace?: never;
1050
+ };
918
1051
  "/v1/orgs/me": {
919
1052
  parameters: {
920
1053
  query?: never;
@@ -9142,6 +9275,8 @@ type PublicOrgResponse = Res<'/v1/orgs/me', 'get'>;
9142
9275
  type CreateUserRequest = Req<'/v1/orgs/me/users', 'post'>;
9143
9276
  type CreateUserResponse = Res<'/v1/orgs/me/users', 'post'>;
9144
9277
  type ListUsersResponse = Res<'/v1/orgs/me/users', 'get'>;
9278
+ type CreateOrgRequest = Req<'/v1/orgs', 'post'>;
9279
+ type CreateOrgResponse = Res<'/v1/orgs', 'post'>;
9145
9280
  type CreateOrganizationRequest = Req<'/v1/admin/organizations', 'post'>;
9146
9281
  type CreateOrganizationResponse = Res<'/v1/admin/organizations', 'post'>;
9147
9282
  type SetAccountTypeRequest = Req<'/v1/admin/organizations/{id}/account-type', 'patch'>;
@@ -9160,11 +9295,11 @@ type SignRequest = Req<'/v1/sign', 'post'>;
9160
9295
  type SignResponse = Res<'/v1/sign', 'post'>;
9161
9296
  type ProvisionContractRequest = Req<'/v1/contracts/provision', 'post'>;
9162
9297
  type ProvisionContractResponse = Res<'/v1/contracts/provision', 'post'>;
9163
- type AddSigningKeyRequest = Req<'/v1/keys', 'post'>;
9298
+ type AddSigningKeyRequest$1 = Req<'/v1/keys', 'post'>;
9164
9299
  type AddSigningKeyResponse = Res<'/v1/keys', 'post'>;
9165
- type RevokeSigningKeyRequest = Req<'/v1/keys/revoke', 'post'>;
9300
+ type RevokeSigningKeyRequest$1 = Req<'/v1/keys/revoke', 'post'>;
9166
9301
  type RevokeSigningKeyResponse = Res<'/v1/keys/revoke', 'post'>;
9167
- type PauseRequest = Req<'/v1/contracts/pause', 'post'>;
9302
+ type PauseRequest$1 = Req<'/v1/contracts/pause', 'post'>;
9168
9303
  type PauseResponse = Res<'/v1/contracts/pause', 'post'>;
9169
9304
  type UnpauseRequest = Req<'/v1/contracts/unpause', 'post'>;
9170
9305
  type UnpauseResponse = Res<'/v1/contracts/unpause', 'post'>;
@@ -9441,6 +9576,17 @@ declare class WitniumchainClient {
9441
9576
  */
9442
9577
  me(): Promise<AccountResponse>;
9443
9578
  signup(body: SignupRequest): Promise<SignupResponse>;
9579
+ /**
9580
+ * Self-serve org creation (Phase RBAC Thread D). One public call that
9581
+ * creates the admin user, the org, the org-admin membership, and deploys
9582
+ * the contract with the caller's client-generated `ownerPublicKey` +
9583
+ * `initialSigningPublicKey`. Witnium never sees the private bytes.
9584
+ *
9585
+ * Returns `{ orgId, userId, contractAddress, contractVersion,
9586
+ * emailVerifyToken }`. The verify token is also emailed; it's echoed here
9587
+ * for UIs that prefer to render the verify link themselves.
9588
+ */
9589
+ createOrg(body: CreateOrgRequest): Promise<CreateOrgResponse>;
9444
9590
  verifyEmail(token: string): Promise<VerifyEmailResponse>;
9445
9591
  login(body: LoginRequest): Promise<LoginResponse>;
9446
9592
  logout(): Promise<LogoutResponse>;
@@ -9465,9 +9611,9 @@ declare class WitniumchainClient {
9465
9611
  revokeDelegatedKey(id: string): Promise<RevokeDelegatedKeyResponse>;
9466
9612
  sign(body: SignRequest, requestId?: string): Promise<SignResponse>;
9467
9613
  provisionContract(body: ProvisionContractRequest): Promise<ProvisionContractResponse>;
9468
- addSigningKey(body: AddSigningKeyRequest): Promise<AddSigningKeyResponse>;
9469
- revokeSigningKey(body: RevokeSigningKeyRequest): Promise<RevokeSigningKeyResponse>;
9470
- pauseContract(body: PauseRequest): Promise<PauseResponse>;
9614
+ addSigningKey(body: AddSigningKeyRequest$1): Promise<AddSigningKeyResponse>;
9615
+ revokeSigningKey(body: RevokeSigningKeyRequest$1): Promise<RevokeSigningKeyResponse>;
9616
+ pauseContract(body: PauseRequest$1): Promise<PauseResponse>;
9471
9617
  unpauseContract(body: UnpauseRequest): Promise<UnpauseResponse>;
9472
9618
  proposeWitness(contractAddress: string, body: ProposeWitnessRequest, idempotencyKey?: string): Promise<ProposeWitnessResponse>;
9473
9619
  signWitness(contractAddress: string, witnessId: string, body: SignWitnessRequest): Promise<SignWitnessResponse>;
@@ -9690,8 +9836,8 @@ declare class SigningKeys {
9690
9836
  * WitniumchainClient.getAccount} and returns the `signingKeys` slice.
9691
9837
  */
9692
9838
  list(): Promise<AccountResponse['signingKeys']>;
9693
- add(body: AddSigningKeyRequest): Promise<AddSigningKeyResponse>;
9694
- revoke(body: RevokeSigningKeyRequest): Promise<RevokeSigningKeyResponse>;
9839
+ add(body: AddSigningKeyRequest$1): Promise<AddSigningKeyResponse>;
9840
+ revoke(body: RevokeSigningKeyRequest$1): Promise<RevokeSigningKeyResponse>;
9695
9841
  }
9696
9842
  /** `client.oauth.sessions.*` — list and revoke active OAuth sessions. */
9697
9843
  declare class OauthNamespace {
@@ -9792,6 +9938,66 @@ declare class WitniumchainAdminClient {
9792
9938
  adjustCredits(orgId: string, delta: number, note?: string): Promise<AdjustCreditsResponse>;
9793
9939
  }
9794
9940
 
9941
+ type Body<T> = T extends {
9942
+ content: {
9943
+ 'application/json': infer J;
9944
+ };
9945
+ } ? J : never;
9946
+ type Resp<T> = T extends {
9947
+ content: {
9948
+ 'application/json': infer J;
9949
+ };
9950
+ } ? J : never;
9951
+ type DeployContractRequest = Body<NonNullable<paths['/v5/contracts/deploy']['post']['requestBody']>>;
9952
+ type DeployContractResponse = Resp<paths['/v5/contracts/deploy']['post']['responses']['200']>;
9953
+ type AddSigningKeyRequest = Body<NonNullable<paths['/v5/contracts/{contractAddress}/keys']['post']['requestBody']>>;
9954
+ type OwnerOpResponse = Resp<paths['/v5/contracts/{contractAddress}/keys']['post']['responses']['200']>;
9955
+ type RevokeSigningKeyRequest = {
9956
+ keyToRevoke: string;
9957
+ ownerNonce: number;
9958
+ ownerSignature: string;
9959
+ };
9960
+ type PauseRequest = {
9961
+ ownerNonce: number;
9962
+ ownerSignature: string;
9963
+ };
9964
+ interface WitniumchainChainAdminClientConfig {
9965
+ /** chain-api base URL, e.g. https://api.witniumchain.com. Trailing slash optional. */
9966
+ baseUrl: string;
9967
+ /**
9968
+ * Static bearer token (legacy `BEARER_TOKEN`). Mutually exclusive with
9969
+ * `adminTokenProvider`. Phase RBAC checkpoint 3 retires this in favour of
9970
+ * the provider.
9971
+ */
9972
+ adminToken?: string;
9973
+ /**
9974
+ * Async function that yields a fresh accounts-minted service-principal JWT
9975
+ * with `aud=https://api.witniumchain.com/admin` + `role=org-admin` on each
9976
+ * call. Implementations typically cache against the token's `exp`. Returned
9977
+ * tokens MUST satisfy chain-api's DashboardJwtGuard.
9978
+ */
9979
+ adminTokenProvider?: () => Promise<string>;
9980
+ /** Per-request timeout in milliseconds. Default 30000. */
9981
+ timeout?: number;
9982
+ /** Alternate fetch implementation, e.g. for tests. Default globalThis.fetch. */
9983
+ fetch?: typeof fetch;
9984
+ }
9985
+ declare class WitniumchainChainAdminClient {
9986
+ private readonly baseUrl;
9987
+ private readonly adminToken;
9988
+ private readonly adminTokenProvider;
9989
+ private readonly timeout;
9990
+ private readonly fetchImpl;
9991
+ constructor(config: WitniumchainChainAdminClientConfig);
9992
+ deployContract(body: DeployContractRequest): Promise<DeployContractResponse>;
9993
+ addSigningKey(contractAddress: string, body: AddSigningKeyRequest): Promise<OwnerOpResponse>;
9994
+ revokeSigningKey(contractAddress: string, body: RevokeSigningKeyRequest): Promise<OwnerOpResponse>;
9995
+ pauseContract(contractAddress: string, body: PauseRequest): Promise<OwnerOpResponse>;
9996
+ unpauseContract(contractAddress: string, body: PauseRequest): Promise<OwnerOpResponse>;
9997
+ private resolveToken;
9998
+ private req;
9999
+ }
10000
+
9795
10001
  /**
9796
10002
  * WitniumchainOrgClient — org-admin facade over the accounts API.
9797
10003
  *
@@ -9864,4 +10070,4 @@ declare class WitniumchainApiError extends Error {
9864
10070
  });
9865
10071
  }
9866
10072
 
9867
- export { type AccountResponse, type AccountType, type paths$1 as AccountsPaths, type AddSigningKeyRequest, type AddSigningKeyResponse, type AdjustCreditsRequest, type AdjustCreditsResponse, type BeginOAuthLoginArgs, type BeginOAuthLoginResult, type paths as ChainPaths, type CheckoutRequest, type CheckoutResponse, type CreateOrganizationRequest, type CreateOrganizationResponse, type CreateUserRequest, type CreateUserResponse, DelegatedKeys, type FinalizeWitnessResponse, type ForgotPasswordRequest, type ForgotPasswordResponse, type GetWitnessResponse, type HealthLiveResponse, type HealthReadyResponse, type LedgerResponse, type ListDelegatedKeysResponse, type ListOauthSessionsResponse, type ListUsersResponse, type LoginRequest, type LoginResponse, type LogoutResponse, MfaNamespace, MfaRecoveryCodes, MfaTotp, type OAuthTokenSnapshot, OauthNamespace, OauthSessions, OrgUsers, type OwnerSigner, type PauseRequest, type PauseResponse, type PkceVerifierStorage, type PortalResponse, type PrepareDelegatedKeyRequest, type PreparedDelegatedKeyResponse, type ProposeWitnessRequest, type ProposeWitnessResponse, type ProvisionContractRequest, type ProvisionContractResponse, type ProvisionDelegatedKeyArgs, type ProvisionDelegatedKeyResult, type PublicOrgResponse, type RecoveryCodesRegenerateResponse, type ResetPasswordRequest, type ResetPasswordResponse, type RevokeDelegatedKeyResponse, type RevokeSigningKeyRequest, type RevokeSigningKeyResponse, type RevokeWitnessRequest, type RevokeWitnessResponse, type RotateApiKeyResponse, type SetAccountTypeRequest, type SetAccountTypeResponse, type SignRequest, type SignResponse, type SignWitnessRequest, type SignWitnessResponse, type SignedRequestSigner, SigningKeys, type SignupRequest, type SignupResponse, type SubmitDelegatedKeyRequest, type SubmitDelegatedKeyResponse, Subscriptions, type TotpConfirmRequest, type TotpConfirmResponse, type TotpDisableResponse, type TotpEnrollResponse, type UnpauseRequest, type UnpauseResponse, type VerifyEmailResponse, type VerifyOrganizationResponse, WitniumchainAdminClient, type WitniumchainAdminClientConfig, WitniumchainApiError, WitniumchainClient, type WitniumchainClientConfig, WitniumchainOrgClient, type WitniumchainOrgClientConfig, defaultVerifierStorage };
10073
+ export { type AccountResponse, type AccountType, type paths$1 as AccountsPaths, type AddSigningKeyRequest$1 as AddSigningKeyRequest, type AddSigningKeyResponse, type AdjustCreditsRequest, type AdjustCreditsResponse, type BeginOAuthLoginArgs, type BeginOAuthLoginResult, type paths as ChainPaths, type CheckoutRequest, type CheckoutResponse, type CreateOrgRequest, type CreateOrgResponse, type CreateOrganizationRequest, type CreateOrganizationResponse, type CreateUserRequest, type CreateUserResponse, DelegatedKeys, type FinalizeWitnessResponse, type ForgotPasswordRequest, type ForgotPasswordResponse, type GetWitnessResponse, type HealthLiveResponse, type HealthReadyResponse, type LedgerResponse, type ListDelegatedKeysResponse, type ListOauthSessionsResponse, type ListUsersResponse, type LoginRequest, type LoginResponse, type LogoutResponse, MfaNamespace, MfaRecoveryCodes, MfaTotp, type OAuthTokenSnapshot, OauthNamespace, OauthSessions, OrgUsers, type OwnerSigner, type PauseRequest$1 as PauseRequest, type PauseResponse, type PkceVerifierStorage, type PortalResponse, type PrepareDelegatedKeyRequest, type PreparedDelegatedKeyResponse, type ProposeWitnessRequest, type ProposeWitnessResponse, type ProvisionContractRequest, type ProvisionContractResponse, type ProvisionDelegatedKeyArgs, type ProvisionDelegatedKeyResult, type PublicOrgResponse, type RecoveryCodesRegenerateResponse, type ResetPasswordRequest, type ResetPasswordResponse, type RevokeDelegatedKeyResponse, type RevokeSigningKeyRequest$1 as RevokeSigningKeyRequest, type RevokeSigningKeyResponse, type RevokeWitnessRequest, type RevokeWitnessResponse, type RotateApiKeyResponse, type SetAccountTypeRequest, type SetAccountTypeResponse, type SignRequest, type SignResponse, type SignWitnessRequest, type SignWitnessResponse, type SignedRequestSigner, SigningKeys, type SignupRequest, type SignupResponse, type SubmitDelegatedKeyRequest, type SubmitDelegatedKeyResponse, Subscriptions, type TotpConfirmRequest, type TotpConfirmResponse, type TotpDisableResponse, type TotpEnrollResponse, type UnpauseRequest, type UnpauseResponse, type VerifyEmailResponse, type VerifyOrganizationResponse, WitniumchainAdminClient, type WitniumchainAdminClientConfig, WitniumchainApiError, WitniumchainChainAdminClient, type WitniumchainChainAdminClientConfig, WitniumchainClient, type WitniumchainClientConfig, WitniumchainOrgClient, type WitniumchainOrgClientConfig, defaultVerifierStorage };