@aithos/sdk 0.1.0-alpha.3 → 0.1.0-alpha.31

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.
Files changed (66) hide show
  1. package/README.md +159 -0
  2. package/dist/src/auth-api.d.ts +149 -0
  3. package/dist/src/auth-api.js +226 -0
  4. package/dist/src/auth.d.ts +436 -67
  5. package/dist/src/auth.js +1098 -69
  6. package/dist/src/compute.d.ts +221 -9
  7. package/dist/src/compute.js +293 -16
  8. package/dist/src/data-schema-contacts-v1.d.ts +14 -0
  9. package/dist/src/data-schema-contacts-v1.js +28 -0
  10. package/dist/src/data.d.ts +97 -0
  11. package/dist/src/data.js +634 -0
  12. package/dist/src/endpoints.d.ts +9 -0
  13. package/dist/src/endpoints.js +5 -0
  14. package/dist/src/ethos.d.ts +202 -1
  15. package/dist/src/ethos.js +821 -16
  16. package/dist/src/index.d.ts +15 -6
  17. package/dist/src/index.js +36 -9
  18. package/dist/src/internal/delegate-bundle.d.ts +18 -0
  19. package/dist/src/internal/delegate-bundle.js +94 -0
  20. package/dist/src/internal/delegate-state.d.ts +45 -0
  21. package/dist/src/internal/delegate-state.js +120 -0
  22. package/dist/src/internal/owner-signers.d.ts +78 -0
  23. package/dist/src/internal/owner-signers.js +179 -0
  24. package/dist/src/internal/protocol-client-bridge.d.ts +8 -0
  25. package/dist/src/internal/protocol-client-bridge.js +20 -0
  26. package/dist/src/internal/recovery-file.d.ts +29 -0
  27. package/dist/src/internal/recovery-file.js +98 -0
  28. package/dist/src/internal/signer.d.ts +59 -0
  29. package/dist/src/internal/signer.js +86 -0
  30. package/dist/src/key-store.d.ts +128 -0
  31. package/dist/src/key-store.js +244 -0
  32. package/dist/src/mandates.d.ts +163 -1
  33. package/dist/src/mandates.js +286 -8
  34. package/dist/src/sdk.d.ts +39 -3
  35. package/dist/src/sdk.js +36 -23
  36. package/dist/src/session-store.d.ts +58 -0
  37. package/dist/src/session-store.js +158 -0
  38. package/dist/src/wallet.d.ts +4 -6
  39. package/dist/src/wallet.js +18 -8
  40. package/dist/src/web.d.ts +279 -0
  41. package/dist/src/web.js +186 -0
  42. package/dist/test/auth-j3.test.d.ts +2 -0
  43. package/dist/test/auth-j3.test.js +391 -0
  44. package/dist/test/compute-delegate-path.test.d.ts +2 -0
  45. package/dist/test/compute-delegate-path.test.js +183 -0
  46. package/dist/test/compute.test.js +26 -11
  47. package/dist/test/endpoints.test.js +20 -1
  48. package/dist/test/ethos-first-edition.test.d.ts +2 -0
  49. package/dist/test/ethos-first-edition.test.js +248 -0
  50. package/dist/test/ethos.test.d.ts +2 -0
  51. package/dist/test/ethos.test.js +219 -0
  52. package/dist/test/key-store.test.d.ts +2 -0
  53. package/dist/test/key-store.test.js +161 -0
  54. package/dist/test/mandates-compute.test.d.ts +2 -0
  55. package/dist/test/mandates-compute.test.js +256 -0
  56. package/dist/test/mandates.test.d.ts +2 -0
  57. package/dist/test/mandates.test.js +93 -0
  58. package/dist/test/sdk.test.js +70 -30
  59. package/dist/test/signer.test.d.ts +2 -0
  60. package/dist/test/signer.test.js +117 -0
  61. package/dist/test/signup-bootstrap.test.d.ts +2 -0
  62. package/dist/test/signup-bootstrap.test.js +222 -0
  63. package/dist/test/wallet.test.js +20 -9
  64. package/dist/test/web.test.d.ts +2 -0
  65. package/dist/test/web.test.js +270 -0
  66. package/package.json +5 -4
package/README.md CHANGED
@@ -55,11 +55,170 @@ const reply = await sdk.compute.invokeBedrock({
55
55
  console.log(reply.content);
56
56
  ```
57
57
 
58
+ ## Delegating compute to an agent — opt-in token spending
59
+
60
+ To let an agent (or another user, or a third-party app) invoke Bedrock
61
+ **in your name**, with **your credits**, you mint a mandate. Token
62
+ spending is its own opt-in capability — passing it is a separate,
63
+ named, validated input that a consent UI can review. It is NEVER an
64
+ implicit side-effect of an ethos read/write scope.
65
+
66
+ ```ts
67
+ // Mint a mandate that lets agent Bob read your public ethos AND
68
+ // spend up to 5 000 microcredits/day on Haiku, capped at 100 000
69
+ // microcredits over the whole mandate lifetime.
70
+ const mandate = await sdk.mandates.create({
71
+ granteeId: "urn:agent:bob",
72
+ scopes: ["ethos.read.public"],
73
+ ttlSeconds: 86_400,
74
+ compute: {
75
+ dailyCapMicrocredits: 5_000,
76
+ totalCapMicrocredits: 100_000,
77
+ maxCreditsPerCall: 500,
78
+ allowedModels: ["claude-haiku-4-5"],
79
+ },
80
+ });
81
+
82
+ // Hand `mandate.bundle` (a `.aithos-delegate.json` Blob) to Bob.
83
+ // He imports it, then signs his own envelopes and calls
84
+ // sdk.compute.invokeBedrock({ mandateId: mandate.mandateId, … })
85
+ // — every invocation debits *your* wallet, capped per the budget
86
+ // you set.
87
+ ```
88
+
89
+ Three invariants the SDK enforces synchronously, before reaching the
90
+ network — they fail fast with a precise `AithosSDKError`:
91
+
92
+ - **No smuggling.** Adding `"compute.invoke"` directly to `scopes[]`
93
+ throws `mandates_invalid_scopes`. The `compute` namespace is the
94
+ only path, so a UI reviewing `compute` can never be bypassed.
95
+ - **No bearer compute.** A `compute` namespace without at least one
96
+ of `dailyCapMicrocredits` or `totalCapMicrocredits` throws
97
+ `mandates_invalid_compute`. Unbounded compute mandates are forbidden
98
+ by construction.
99
+ - **Compute-only is fine.** `scopes: []` is allowed when `compute` is
100
+ set — useful for agents that only consume tokens (e.g. creative
101
+ assistants) without seeing any of your data.
102
+
103
+ ## Custodial auth — onboarding users without a recovery file
104
+
105
+ Three new methods on `AithosAuth` let an app create and authenticate
106
+ its end-users via a server-managed custody flow — the user only needs
107
+ an email address and a password sent by mail. No recovery file, no
108
+ Google account, no client-side cryptography to handle.
109
+
110
+ The model is honest custody: Aithos KMS-wraps the user's Ed25519
111
+ identity seeds, and unwraps them on every sign-in after password
112
+ verification. Equivalent to how Coinbase or any hosted SaaS keeps your
113
+ private key. Annunciated to the user in the welcome email.
114
+
115
+ ```ts
116
+ import { AithosSDK } from "@aithos/sdk";
117
+
118
+ // ─── Server-side: sign-up ───────────────────────────────────────────
119
+ // MUST run on your backend. The API key is a server secret —
120
+ // provisioned by Aithos via the operator runbook.
121
+ const sdk = new AithosSDK({ identity });
122
+ const result = await sdk.auth.signUpCustodial({
123
+ apiKey: process.env.AITHOS_API_KEY!,
124
+ email: "alice@example.com",
125
+ displayName: "Alice",
126
+ });
127
+ // → { userId, did, handle, email, mailSent }
128
+ // The user receives an email with their password and a sign-in link.
129
+
130
+ // ─── Browser-side: sign-in ──────────────────────────────────────────
131
+ // User pastes the password from their mail into your sign-in form,
132
+ // then your frontend calls this. No API key needed — the password
133
+ // is the credential.
134
+ const { session, passwordMustChange } = await sdk.auth.signInCustodial({
135
+ email: "alice@example.com",
136
+ password: "MyTempPass32chars",
137
+ });
138
+ // Local KeyStore is now hydrated with the 4 Ed25519 sphere seeds —
139
+ // the user can publish ethos editions, mint mandates, invoke compute,
140
+ // exactly as if they had signed in via a recovery file or Google SSO.
141
+ if (passwordMustChange) {
142
+ // Optional: nudge the user to set their own password via the
143
+ // standard reset flow.
144
+ }
145
+
146
+ // ─── Browser-side: request password reset ───────────────────────────
147
+ // The backend always returns silently (anti-enumeration). If the email
148
+ // is registered AND in custodial mode AND not in cooldown AND under the
149
+ // daily cap, a magic-link email is sent to the address.
150
+ await sdk.auth.requestPasswordReset({ email: "alice@example.com" });
151
+ ```
152
+
153
+ The reset finalization (collecting the new password from the user) is
154
+ done on a small web page hosted by Aithos at `https://app.aithos.be/reset`
155
+ (or your app's own `reset_base_url` if you've registered one — see the
156
+ operator runbook). The page POSTs to `/auth/custodial/reset/finalize`
157
+ and returns the user to your sign-in page on success.
158
+
159
+ ### Getting an API key
160
+
161
+ API keys are provisioned out-of-band by Aithos. Contact the maintainer
162
+ (or use the self-service console at `aithos.be/console` when it ships
163
+ in V2). The pattern is `aithos_<env>_<32 chars b58>`. Keep it in your
164
+ backend's secrets manager — never in browser code.
165
+
166
+ ### Trade-offs vs. the zk and Google SSO flows
167
+
168
+ | | zk (recovery file) | Google SSO (KMS) | **Custodial** |
169
+ |----------------|----------------------------|----------------------|---------------|
170
+ | User burden | downloads `recovery.json` | Google consent | email only |
171
+ | Password reset | requires recovery file | re-auth via Google | magic-link mail |
172
+ | Trust model | zero-knowledge (you only) | Aithos + Google | Aithos only |
173
+ | Multi-device | re-import recovery | re-Google | email + password |
174
+ | SDK signing capability | full | full | full |
175
+
176
+ Custodial is the right default for SDK-integrated apps that want
177
+ SaaS-grade UX. zk is the right default for power users who want
178
+ sovereign custody. SSO is the right default for users already invested
179
+ in the Google ecosystem.
180
+
181
+ ## Extracting webpages without an LLM
182
+
183
+ `sdk.web` is a token-priced primitive that lets your agent read a
184
+ public webpage and get back cleaned HTML, purged CSS and a
185
+ deterministic visual signature — all computed server-side without an
186
+ LLM in the loop. Pricing is a flat **1 microcredit** per successful
187
+ extraction (refunded on failure), versus ~30 mc for a comparable
188
+ LLM-based extraction.
189
+
190
+ ```ts
191
+ import { AithosSDK } from "@aithos/sdk";
192
+
193
+ const sdk = new AithosSDK({ auth, appDid });
194
+
195
+ const { data, creditsCharged } = await sdk.web.extract({
196
+ url: "https://example.com",
197
+ });
198
+
199
+ console.log(data.meta.title); // "Example Domain"
200
+ console.log(data.visual_signature.colors.primary); // "#0078d4"
201
+ console.log(data.styles.css.length); // purged + minified CSS
202
+ ```
203
+
204
+ Owners can mint a mandate for delegate-only extraction:
205
+
206
+ ```ts
207
+ import { WEB_EXTRACT_SCOPE } from "@aithos/sdk";
208
+
209
+ await sdk.mandates.create({
210
+ appDid: "did:aithos:app:my-agent",
211
+ scopes: [WEB_EXTRACT_SCOPE],
212
+ // ...
213
+ });
214
+ ```
215
+
58
216
  ## What lives where
59
217
 
60
218
  | Namespace | Purpose |
61
219
  | ---------------- | ------------------------------------------------------------------------------------------ |
62
220
  | `sdk.compute` | Bedrock invocation through the Aithos compute proxy (signed envelope, wallet enforcement). |
221
+ | `sdk.web` | Webpage extraction without an LLM through the web extractor proxy (1 mc / call). |
63
222
  | `sdk.wallet` | Stripe Checkout sessions for credit-pack top-ups, balance helpers. |
64
223
  | `sdk.ethos` | Ethos-zone composition / parsing — re-exported from `@aithos/protocol-client`. |
65
224
  | `sdk.onboarding` | First-run identity / DID flows — re-exported. |
@@ -0,0 +1,149 @@
1
+ import { type KdfParams } from "@aithos/protocol-client";
2
+ interface HttpClient {
3
+ readonly fetchImpl: typeof fetch;
4
+ readonly authBaseUrl: string;
5
+ }
6
+ export interface RegisterApiInput {
7
+ readonly email: string;
8
+ readonly handle: string;
9
+ readonly displayName: string;
10
+ readonly did: string;
11
+ readonly authKey: Uint8Array;
12
+ readonly authSalt: Uint8Array;
13
+ readonly encSalt: Uint8Array;
14
+ readonly kdf: KdfParams;
15
+ readonly blob: Uint8Array;
16
+ readonly blobNonce: Uint8Array;
17
+ readonly blobVersion: number;
18
+ }
19
+ export interface RegisterApiResponse {
20
+ readonly session: string;
21
+ readonly exp: number;
22
+ }
23
+ export declare function registerAccount(http: HttpClient, input: RegisterApiInput): Promise<RegisterApiResponse>;
24
+ export interface PutBlobApiInput {
25
+ readonly jwt: string;
26
+ readonly blob: Uint8Array;
27
+ readonly blobNonce: Uint8Array;
28
+ readonly blobVersion: number;
29
+ }
30
+ export declare function putBlob(http: HttpClient, input: PutBlobApiInput): Promise<{
31
+ ok: true;
32
+ }>;
33
+ export interface LoginChallengeResponse {
34
+ readonly authSalt: Uint8Array;
35
+ readonly encSalt: Uint8Array;
36
+ readonly kdf: KdfParams;
37
+ }
38
+ export declare function loginChallenge(http: HttpClient, email: string): Promise<LoginChallengeResponse>;
39
+ export interface LoginVerifyResponse {
40
+ readonly session: string;
41
+ readonly exp: number;
42
+ readonly did: string;
43
+ readonly handle: string;
44
+ readonly blob: Uint8Array;
45
+ readonly blobNonce: Uint8Array;
46
+ readonly blobVersion: number;
47
+ }
48
+ export declare function loginVerify(http: HttpClient, email: string, authKey: Uint8Array): Promise<LoginVerifyResponse>;
49
+ /**
50
+ * Input for {@link custodialSignUp}. Caller authenticates the app via ONE
51
+ * of `apiKey` (server-only secret) or `publicKey` (browser-safe). The
52
+ * user's `password` is always required — sign-up no longer auto-generates
53
+ * one server-side. The created account starts in a *pending* state and
54
+ * the user must click the link sent to their inbox before they can
55
+ * sign in.
56
+ */
57
+ export interface CustodialSignUpApiInput {
58
+ /** Server-only Bearer secret: `aithos_<env>_<…>`. Mutually exclusive
59
+ * with `publicKey`. Use this from your backend. */
60
+ readonly apiKey?: string;
61
+ /** Browser-safe public client key: `pk_<env>_<…>`. Mutually exclusive
62
+ * with `apiKey`. The browser sends its `Origin` header alongside; the
63
+ * Aithos backend matches it against the app's allowed_origins list. */
64
+ readonly publicKey?: string;
65
+ readonly email: string;
66
+ /** Raw password the user chose. Must be ≥ 10 chars and mix letters
67
+ * with at least one digit or symbol (server-side rule). */
68
+ readonly password: string;
69
+ readonly displayName?: string;
70
+ readonly handleHint?: string;
71
+ }
72
+ export interface CustodialSignUpApiResponse {
73
+ /** Always "pending_verification" — sign-in is blocked until the
74
+ * user clicks the confirmation link. */
75
+ readonly status: "pending_verification";
76
+ readonly email: string;
77
+ readonly mailSent: boolean;
78
+ readonly mailMessageId?: string;
79
+ }
80
+ /**
81
+ * Provision a custodial-mode account on behalf of a registered app.
82
+ *
83
+ * Two integration paths:
84
+ * - **Backend** caller passes `apiKey` (server-only secret).
85
+ * - **Browser** caller passes `publicKey` (safe to ship in the bundle).
86
+ * The browser also sends its `Origin` header automatically and the
87
+ * auth backend validates it against the app's allowed_origins list.
88
+ *
89
+ * On success the account exists in DDB with `email_verified: false` and
90
+ * the response carries `status: "pending_verification"` — call
91
+ * {@link custodialVerifyEmail} after the user clicks the confirmation
92
+ * link before attempting sign-in.
93
+ */
94
+ export declare function custodialSignUp(http: HttpClient, input: CustodialSignUpApiInput): Promise<CustodialSignUpApiResponse>;
95
+ export interface CustodialVerifyEmailApiInput {
96
+ readonly email: string;
97
+ readonly token: string;
98
+ }
99
+ /** Consume the verification token from the confirmation link. Idempotent
100
+ * on repeated clicks; throws `auth_token_invalid_or_expired` if the
101
+ * token is wrong, consumed, or past its 24h TTL. */
102
+ export declare function custodialVerifyEmail(http: HttpClient, input: CustodialVerifyEmailApiInput): Promise<{
103
+ ok: true;
104
+ }>;
105
+ /** Re-send the verification mail for a pending account. The backend
106
+ * is anti-enum (always 200) and rate-limited 1/h/account, so this is
107
+ * safe to call even when the user state is unknown. Accepts the same
108
+ * credential families as {@link custodialSignUp}. */
109
+ export declare function custodialResendVerify(http: HttpClient, args: {
110
+ readonly email: string;
111
+ readonly apiKey?: string;
112
+ readonly publicKey?: string;
113
+ }): Promise<void>;
114
+ export interface CustodialSignInApiInput {
115
+ readonly email: string;
116
+ readonly password: string;
117
+ }
118
+ export interface CustodialSignInApiResponse {
119
+ readonly session: string;
120
+ readonly exp: number;
121
+ readonly did: string;
122
+ readonly handle: string;
123
+ readonly displayName: string;
124
+ /** Raw 32-byte Ed25519 seed — caller MUST hydrate its keystore and
125
+ * zeroize this buffer. */
126
+ readonly seed: Uint8Array;
127
+ /** Raw 32-byte vault encryption key — same lifecycle. */
128
+ readonly encKey: Uint8Array;
129
+ readonly blob: Uint8Array;
130
+ readonly blobNonce: Uint8Array;
131
+ readonly blobVersion: number;
132
+ readonly passwordMustChange: boolean;
133
+ }
134
+ export declare function custodialSignIn(http: HttpClient, input: CustodialSignInApiInput): Promise<CustodialSignInApiResponse>;
135
+ export declare function custodialResetRequest(http: HttpClient, email: string): Promise<void>;
136
+ export interface CustodialResetFinalizeApiInput {
137
+ readonly email: string;
138
+ readonly token: string;
139
+ readonly newPassword: string;
140
+ }
141
+ export interface CustodialResetFinalizeApiResponse {
142
+ readonly session: string;
143
+ readonly exp: number;
144
+ readonly did: string;
145
+ readonly handle: string;
146
+ }
147
+ export declare function custodialResetFinalize(http: HttpClient, input: CustodialResetFinalizeApiInput): Promise<CustodialResetFinalizeApiResponse>;
148
+ export {};
149
+ //# sourceMappingURL=auth-api.d.ts.map
@@ -0,0 +1,226 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 Mathieu Colla
3
+ // Thin HTTP client over the Aithos auth Lambda.
4
+ //
5
+ // Internal — not exported from the package's public surface. The
6
+ // {@link AithosAuth} class composes these calls with the crypto
7
+ // primitives in `@aithos/protocol-client` and the session store in
8
+ // {@link ./session-store.ts} to expose a high-level
9
+ // signIn / signUp / signInWithGoogle API.
10
+ //
11
+ // Wire format mirrors `aithos/auth/API.md` exactly. All `*_b64` fields
12
+ // are standard-base64 (not URL-safe), padding stripped — see
13
+ // `bytesToB64` / `b64ToBytes` in `@aithos/protocol-client`.
14
+ import { bytesToB64, b64ToBytes, } from "@aithos/protocol-client";
15
+ import { AithosSDKError } from "./types.js";
16
+ async function readError(res, defaultCode) {
17
+ let body = null;
18
+ try {
19
+ body = (await res.json());
20
+ }
21
+ catch {
22
+ /* body not JSON */
23
+ }
24
+ const code = typeof body?.code === "string" ? `auth_${body.code}` : `auth_${defaultCode}`;
25
+ const message = body?.error ?? `${res.status} ${res.statusText || "request failed"}`;
26
+ return new AithosSDKError(code, message, {
27
+ status: res.status,
28
+ ...(body !== null ? { data: body } : {}),
29
+ });
30
+ }
31
+ async function postJson(http, path, body, jwt) {
32
+ const res = await http.fetchImpl(`${http.authBaseUrl}${path}`, {
33
+ method: "POST",
34
+ headers: {
35
+ "content-type": "application/json",
36
+ ...(jwt ? { authorization: `Bearer ${jwt}` } : {}),
37
+ },
38
+ body: JSON.stringify(body),
39
+ });
40
+ if (!res.ok)
41
+ throw await readError(res, "request_failed");
42
+ return (await res.json());
43
+ }
44
+ async function putJson(http, path, body, jwt) {
45
+ const res = await http.fetchImpl(`${http.authBaseUrl}${path}`, {
46
+ method: "PUT",
47
+ headers: {
48
+ "content-type": "application/json",
49
+ authorization: `Bearer ${jwt}`,
50
+ },
51
+ body: JSON.stringify(body),
52
+ });
53
+ if (!res.ok)
54
+ throw await readError(res, "request_failed");
55
+ return (await res.json());
56
+ }
57
+ export async function registerAccount(http, input) {
58
+ return postJson(http, "/auth/register", {
59
+ email: input.email,
60
+ handle: input.handle,
61
+ display_name: input.displayName,
62
+ did: input.did,
63
+ auth_key_b64: bytesToB64(input.authKey),
64
+ auth_salt_b64: bytesToB64(input.authSalt),
65
+ enc_salt_b64: bytesToB64(input.encSalt),
66
+ kdf: input.kdf,
67
+ blob_b64: bytesToB64(input.blob),
68
+ blob_nonce_b64: bytesToB64(input.blobNonce),
69
+ blob_version: input.blobVersion,
70
+ });
71
+ }
72
+ export async function putBlob(http, input) {
73
+ return putJson(http, "/auth/blob", {
74
+ blob_b64: bytesToB64(input.blob),
75
+ blob_nonce_b64: bytesToB64(input.blobNonce),
76
+ blob_version: input.blobVersion,
77
+ }, input.jwt);
78
+ }
79
+ export async function loginChallenge(http, email) {
80
+ const wire = await postJson(http, "/auth/login/challenge", { email });
81
+ return {
82
+ authSalt: b64ToBytes(wire.auth_salt_b64),
83
+ encSalt: b64ToBytes(wire.enc_salt_b64),
84
+ kdf: wire.kdf,
85
+ };
86
+ }
87
+ export async function loginVerify(http, email, authKey) {
88
+ const wire = await postJson(http, "/auth/login/verify", {
89
+ email,
90
+ auth_key_b64: bytesToB64(authKey),
91
+ });
92
+ return {
93
+ session: wire.session,
94
+ exp: wire.exp,
95
+ did: wire.did,
96
+ handle: wire.handle,
97
+ blob: b64ToBytes(wire.blob_b64),
98
+ blobNonce: b64ToBytes(wire.blob_nonce_b64),
99
+ blobVersion: wire.blob_version,
100
+ };
101
+ }
102
+ /**
103
+ * Provision a custodial-mode account on behalf of a registered app.
104
+ *
105
+ * Two integration paths:
106
+ * - **Backend** caller passes `apiKey` (server-only secret).
107
+ * - **Browser** caller passes `publicKey` (safe to ship in the bundle).
108
+ * The browser also sends its `Origin` header automatically and the
109
+ * auth backend validates it against the app's allowed_origins list.
110
+ *
111
+ * On success the account exists in DDB with `email_verified: false` and
112
+ * the response carries `status: "pending_verification"` — call
113
+ * {@link custodialVerifyEmail} after the user clicks the confirmation
114
+ * link before attempting sign-in.
115
+ */
116
+ export async function custodialSignUp(http, input) {
117
+ if (!input.apiKey && !input.publicKey) {
118
+ throw new AithosSDKError("auth_missing_api_key", "signUpCustodial requires either apiKey or publicKey");
119
+ }
120
+ if (input.apiKey && input.publicKey) {
121
+ throw new AithosSDKError("auth_invalid_input", "signUpCustodial: pass exactly one of apiKey or publicKey, not both");
122
+ }
123
+ const bearer = (input.apiKey ?? input.publicKey);
124
+ const res = await http.fetchImpl(`${http.authBaseUrl}/auth/custodial/sign-up`, {
125
+ method: "POST",
126
+ headers: {
127
+ "content-type": "application/json",
128
+ authorization: `Bearer ${bearer}`,
129
+ },
130
+ body: JSON.stringify({
131
+ email: input.email,
132
+ password: input.password,
133
+ ...(input.displayName ? { display_name: input.displayName } : {}),
134
+ ...(input.handleHint ? { handle_hint: input.handleHint } : {}),
135
+ }),
136
+ });
137
+ if (!res.ok)
138
+ throw await readError(res, "custodial_signup_failed");
139
+ const wire = (await res.json());
140
+ return {
141
+ status: "pending_verification",
142
+ email: wire.email,
143
+ mailSent: wire.mail_sent,
144
+ ...(wire.mail_message_id !== undefined
145
+ ? { mailMessageId: wire.mail_message_id }
146
+ : {}),
147
+ };
148
+ }
149
+ /** Consume the verification token from the confirmation link. Idempotent
150
+ * on repeated clicks; throws `auth_token_invalid_or_expired` if the
151
+ * token is wrong, consumed, or past its 24h TTL. */
152
+ export async function custodialVerifyEmail(http, input) {
153
+ const res = await http.fetchImpl(`${http.authBaseUrl}/auth/custodial/verify`, {
154
+ method: "POST",
155
+ headers: { "content-type": "application/json" },
156
+ body: JSON.stringify({ email: input.email, token: input.token }),
157
+ });
158
+ if (!res.ok)
159
+ throw await readError(res, "custodial_verify_failed");
160
+ return (await res.json());
161
+ }
162
+ /* ---- POST /auth/custodial/verify/resend -------------------------------- */
163
+ /** Re-send the verification mail for a pending account. The backend
164
+ * is anti-enum (always 200) and rate-limited 1/h/account, so this is
165
+ * safe to call even when the user state is unknown. Accepts the same
166
+ * credential families as {@link custodialSignUp}. */
167
+ export async function custodialResendVerify(http, args) {
168
+ if (!args.apiKey && !args.publicKey) {
169
+ throw new AithosSDKError("auth_missing_api_key", "resendVerificationEmail requires either apiKey or publicKey");
170
+ }
171
+ const bearer = (args.apiKey ?? args.publicKey);
172
+ const res = await http.fetchImpl(`${http.authBaseUrl}/auth/custodial/verify/resend`, {
173
+ method: "POST",
174
+ headers: {
175
+ "content-type": "application/json",
176
+ authorization: `Bearer ${bearer}`,
177
+ },
178
+ body: JSON.stringify({ email: args.email }),
179
+ });
180
+ if (!res.ok)
181
+ throw await readError(res, "custodial_resend_failed");
182
+ }
183
+ export async function custodialSignIn(http, input) {
184
+ const wire = await postJson(http, "/auth/custodial/sign-in", { email: input.email, password: input.password });
185
+ return {
186
+ session: wire.session,
187
+ exp: wire.exp,
188
+ did: wire.did,
189
+ handle: wire.handle,
190
+ displayName: wire.display_name,
191
+ seed: b64ToBytes(wire.seed_b64),
192
+ encKey: b64ToBytes(wire.enc_key_b64),
193
+ blob: wire.blob_b64 ? b64ToBytes(wire.blob_b64) : new Uint8Array(0),
194
+ blobNonce: wire.blob_nonce_b64
195
+ ? b64ToBytes(wire.blob_nonce_b64)
196
+ : new Uint8Array(0),
197
+ blobVersion: wire.blob_version,
198
+ passwordMustChange: wire.password_must_change,
199
+ };
200
+ }
201
+ /* ---- POST /auth/custodial/reset/request --------------------------------- */
202
+ export async function custodialResetRequest(http, email) {
203
+ // Backend always returns 200 { ok: true } regardless. We accept any
204
+ // 2xx body, even non-JSON, to be defensive.
205
+ const res = await http.fetchImpl(`${http.authBaseUrl}/auth/custodial/reset/request`, {
206
+ method: "POST",
207
+ headers: { "content-type": "application/json" },
208
+ body: JSON.stringify({ email }),
209
+ });
210
+ if (!res.ok)
211
+ throw await readError(res, "custodial_reset_request_failed");
212
+ }
213
+ export async function custodialResetFinalize(http, input) {
214
+ const wire = await postJson(http, "/auth/custodial/reset/finalize", {
215
+ email: input.email,
216
+ token: input.token,
217
+ new_password: input.newPassword,
218
+ });
219
+ return {
220
+ session: wire.session,
221
+ exp: wire.exp,
222
+ did: wire.did,
223
+ handle: wire.handle,
224
+ };
225
+ }
226
+ //# sourceMappingURL=auth-api.js.map