@aithos/sdk 0.1.0-alpha.4 → 0.1.0-alpha.40

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 (62) hide show
  1. package/README.md +211 -7
  2. package/dist/src/assets.d.ts +207 -0
  3. package/dist/src/assets.js +533 -0
  4. package/dist/src/auth-api.d.ts +138 -0
  5. package/dist/src/auth-api.js +168 -0
  6. package/dist/src/auth.d.ts +536 -119
  7. package/dist/src/auth.js +1207 -152
  8. package/dist/src/compute.d.ts +221 -9
  9. package/dist/src/compute.js +293 -16
  10. package/dist/src/data-schema-contacts-v1.d.ts +14 -0
  11. package/dist/src/data-schema-contacts-v1.js +28 -0
  12. package/dist/src/data.d.ts +153 -0
  13. package/dist/src/data.js +670 -0
  14. package/dist/src/endpoints.d.ts +9 -0
  15. package/dist/src/endpoints.js +5 -0
  16. package/dist/src/ethos.d.ts +202 -1
  17. package/dist/src/ethos.js +821 -16
  18. package/dist/src/index.d.ts +16 -6
  19. package/dist/src/index.js +33 -6
  20. package/dist/src/internal/delegate-bundle.d.ts +18 -0
  21. package/dist/src/internal/delegate-bundle.js +94 -0
  22. package/dist/src/internal/delegate-state.d.ts +45 -0
  23. package/dist/src/internal/delegate-state.js +120 -0
  24. package/dist/src/internal/envelope.d.ts +77 -0
  25. package/dist/src/internal/envelope.js +154 -0
  26. package/dist/src/internal/owner-signers.d.ts +78 -0
  27. package/dist/src/internal/owner-signers.js +179 -0
  28. package/dist/src/internal/protocol-client-bridge.d.ts +8 -0
  29. package/dist/src/internal/protocol-client-bridge.js +20 -0
  30. package/dist/src/internal/recovery-file.d.ts +29 -0
  31. package/dist/src/internal/recovery-file.js +98 -0
  32. package/dist/src/internal/signer.d.ts +59 -0
  33. package/dist/src/internal/signer.js +86 -0
  34. package/dist/src/key-store.d.ts +128 -0
  35. package/dist/src/key-store.js +244 -0
  36. package/dist/src/mandates.d.ts +163 -1
  37. package/dist/src/mandates.js +286 -8
  38. package/dist/src/react/AithosAsset.d.ts +66 -0
  39. package/dist/src/react/AithosAsset.js +67 -0
  40. package/dist/src/react/context.d.ts +29 -0
  41. package/dist/src/react/context.js +31 -0
  42. package/dist/src/react/index.d.ts +28 -0
  43. package/dist/src/react/index.js +30 -0
  44. package/dist/src/react/use-aithos-asset.d.ts +39 -0
  45. package/dist/src/react/use-aithos-asset.js +118 -0
  46. package/dist/src/sdk.d.ts +39 -3
  47. package/dist/src/sdk.js +36 -23
  48. package/dist/src/wallet.d.ts +4 -6
  49. package/dist/src/wallet.js +18 -8
  50. package/dist/src/web.d.ts +279 -0
  51. package/dist/src/web.js +186 -0
  52. package/package.json +18 -3
  53. package/dist/test/auth.test.d.ts +0 -2
  54. package/dist/test/auth.test.js +0 -175
  55. package/dist/test/compute.test.d.ts +0 -2
  56. package/dist/test/compute.test.js +0 -179
  57. package/dist/test/endpoints.test.d.ts +0 -2
  58. package/dist/test/endpoints.test.js +0 -43
  59. package/dist/test/sdk.test.d.ts +0 -2
  60. package/dist/test/sdk.test.js +0 -86
  61. package/dist/test/wallet.test.d.ts +0 -2
  62. package/dist/test/wallet.test.js +0 -110
package/README.md CHANGED
@@ -55,15 +55,219 @@ 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
+
216
+ ## Calling a third-party Aithos-aware backend
217
+
218
+ If your app talks to its own backend (a service you built that verifies
219
+ Aithos envelopes per spec §11.2 using
220
+ `@aithos/protocol-core/envelope`), use `sdk.auth.signEnvelope` to sign
221
+ the request with the same primitive that SDK namespaces use internally
222
+ for `api.aithos.be`. No JWT, no shadow session — the user's DID in the
223
+ envelope's `iss` field is the identity.
224
+
225
+ ```ts
226
+ import { AithosSDK, type SignedEnvelope } from "@aithos/sdk";
227
+
228
+ // Sign a request to your own backend with the active owner's
229
+ // public-sphere key. Default TTL is 60 s.
230
+ const envelope: SignedEnvelope = await sdk.auth.signEnvelope({
231
+ aud: "https://api.example.com/v1/widgets",
232
+ method: "myapp.widgets.create",
233
+ params: { name: "Widget #1" },
234
+ });
235
+
236
+ await fetch("https://api.example.com/v1/widgets", {
237
+ method: "POST",
238
+ headers: { "content-type": "application/json" },
239
+ body: JSON.stringify({
240
+ jsonrpc: "2.0",
241
+ id: crypto.randomUUID(),
242
+ method: "myapp.widgets.create",
243
+ params: { name: "Widget #1", _envelope: envelope },
244
+ }),
245
+ });
246
+ ```
247
+
248
+ The envelope binds the signature to `(iss, aud, method, params_hash,
249
+ nonce, iat, exp)`, so a single envelope cannot be replayed against a
250
+ different endpoint, method, or payload. Throws
251
+ `AithosSDKError("auth_not_signed_in")` if no owner is loaded; throws
252
+ `AithosSDKError("auth_invalid_sphere")` if you pass a sphere outside
253
+ `"root" | "public" | "circle" | "self"` (default is `"public"`).
254
+
255
+ Server-side, your backend verifies the envelope with
256
+ `@aithos/protocol-core`'s `verifyEnvelope` (the 9-step check from spec
257
+ §11.4) — same algorithm that `api.aithos.be` uses, no re-implementation
258
+ needed.
259
+
58
260
  ## What lives where
59
261
 
60
- | Namespace | Purpose |
61
- | ---------------- | ------------------------------------------------------------------------------------------ |
62
- | `sdk.compute` | Bedrock invocation through the Aithos compute proxy (signed envelope, wallet enforcement). |
63
- | `sdk.wallet` | Stripe Checkout sessions for credit-pack top-ups, balance helpers. |
64
- | `sdk.ethos` | Ethos-zone composition / parsing re-exported from `@aithos/protocol-client`. |
65
- | `sdk.onboarding` | First-run identity / DID flows re-exported. |
66
- | `sdk.mandates` | Mint / verify mandates — re-exported. |
262
+ | Namespace | Purpose |
263
+ | -------------------------- | ------------------------------------------------------------------------------------------ |
264
+ | `sdk.auth` | Sign-in, sign-up, key custody and `signEnvelope` for calls to your own Aithos-aware backend. |
265
+ | `sdk.compute` | Bedrock invocation through the Aithos compute proxy (signed envelope, wallet enforcement). |
266
+ | `sdk.web` | Webpage extraction without an LLM through the web extractor proxy (1 mc / call). |
267
+ | `sdk.wallet` | Stripe Checkout sessions for credit-pack top-ups, balance helpers. |
268
+ | `sdk.ethos` | Ethos-zone composition / parsing — re-exported from `@aithos/protocol-client`. |
269
+ | `sdk.onboarding` | First-run identity / DID flows — re-exported. |
270
+ | `sdk.mandates` | Mint / verify mandates — re-exported. |
67
271
 
68
272
  ## License
69
273
 
@@ -0,0 +1,207 @@
1
+ /**
2
+ * `sdk.assets` — high-level API for the Aithos assets sub-protocol PDS.
3
+ *
4
+ * Stores binary content (images, PDFs, audio, video) owned by a
5
+ * subject, encrypted client-side under per-asset AMKs (Asset Master
6
+ * Keys), accessible to authorized apps via signed mandates (v0.2).
7
+ *
8
+ * const assets = sdk.assets;
9
+ * const avatar = await assets.upload({
10
+ * bytes: pngBuffer,
11
+ * mediaType: "image/png",
12
+ * attachTo: { ethos: { zone: "public", sectionId: "sec_identity" } },
13
+ * });
14
+ * // avatar.url is a stable CloudFront URL (public asset)
15
+ *
16
+ * const cv = await assets.upload({
17
+ * bytes: pdfBuffer,
18
+ * mediaType: "application/pdf",
19
+ * attachTo: { ethos: { zone: "circle", sectionId: "sec_career_docs" } },
20
+ * });
21
+ * // cv is private: stored encrypted, fetched via short-lived presigned URL.
22
+ *
23
+ * const bytes = await assets.fetch(cv.urn);
24
+ * // decrypted plaintext returned to the caller
25
+ *
26
+ * The module wires:
27
+ * - AMK generation + wrap (via @aithos/assets-crypto)
28
+ * - Bytes encryption with the canonical nonce-prefix on-disk layout
29
+ * - RecipientResolver (v0.2-ethos: maps {zone} → recipient set)
30
+ * - Direct S3 PUT against the presigned URL returned by init_upload
31
+ * - In-memory AMK cache (per asset URN) for sub-second re-fetches
32
+ * - Signed envelope JSON-RPC dispatch to /mcp/primitives/{read,write}
33
+ *
34
+ * Spec ref: spec/assets/ in the aithos-protocol repo.
35
+ */
36
+ import { type AssetMetadata, type AssetReference } from "@aithos/assets-crypto";
37
+ export interface CreateAssetsClientArgs {
38
+ /** Base URL of the deployed assets PDS. */
39
+ readonly pdsUrl: string;
40
+ /** Subject DID. */
41
+ readonly did: string;
42
+ /** Ed25519 sphere seed (32 bytes). */
43
+ readonly sphereSeed: Uint8Array;
44
+ /** Verification method URL within the DID document (e.g. `<did>#<multibase>` for did:key). */
45
+ readonly verificationMethod: string;
46
+ /** Optional fetch implementation. Defaults to globalThis.fetch. */
47
+ readonly fetch?: typeof fetch;
48
+ /**
49
+ * Optional override for the recipient resolver. By default the SDK
50
+ * uses a "self-only" resolver that maps every private upload to the
51
+ * subject's own X25519 sphere key. Apps that already orchestrate
52
+ * grantees explicitly may pass a custom resolver.
53
+ */
54
+ readonly recipientResolver?: RecipientResolver;
55
+ }
56
+ export interface AttachedContext {
57
+ readonly ethos?: {
58
+ readonly zone: "public" | "circle" | "self";
59
+ readonly sectionId?: string;
60
+ };
61
+ readonly data?: {
62
+ readonly collectionUrn: string;
63
+ readonly recordId?: string;
64
+ readonly field?: string;
65
+ };
66
+ }
67
+ export interface AssetUploadInput {
68
+ readonly bytes: Uint8Array;
69
+ readonly mediaType: string;
70
+ readonly attachTo?: AttachedContext;
71
+ /**
72
+ * OPTIONAL — force a regime. Defaults to "auto":
73
+ * - ethos.zone === "public" → public
74
+ * - anything else → private
75
+ */
76
+ readonly regime?: "auto" | "public" | "private";
77
+ /** OPTIONAL — strict forward secrecy at AMK rotation time. */
78
+ readonly forwardSecrecy?: "best_effort" | "strict";
79
+ }
80
+ export interface AssetUploadResult {
81
+ readonly urn: string;
82
+ readonly assetId: string;
83
+ readonly mediaType: string;
84
+ readonly sizeBytes: number;
85
+ readonly sha256OfPlaintext: string;
86
+ readonly encrypted: boolean;
87
+ /**
88
+ * Stable URL for public assets (CloudFront-served). Absent for
89
+ * private assets — fetch them via {@link AssetsClient.fetch}.
90
+ */
91
+ readonly url?: string;
92
+ /** Whether this URN was returned by intra-subject dedup (existing asset). */
93
+ readonly dedupHit: boolean;
94
+ }
95
+ export interface AssetFetchResult {
96
+ readonly urn: string;
97
+ readonly mediaType: string;
98
+ readonly sizeBytes: number;
99
+ readonly bytes: Uint8Array;
100
+ readonly sha256OfPlaintext: string;
101
+ }
102
+ export interface AssetBrief {
103
+ readonly urn: string;
104
+ readonly assetId: string;
105
+ readonly mediaType: string;
106
+ readonly sizeBytes: number;
107
+ readonly sha256OfPlaintext: string;
108
+ readonly encrypted: boolean;
109
+ readonly state: "ACTIVE" | "ORPHANED" | "TOMBSTONED";
110
+ readonly referenceCount: number;
111
+ readonly createdAt: string;
112
+ readonly modifiedAt: string;
113
+ }
114
+ export interface ListAssetsOpts {
115
+ readonly filter?: {
116
+ readonly mediaTypePrefix?: string;
117
+ readonly sizeBytes?: {
118
+ gte?: number;
119
+ lte?: number;
120
+ };
121
+ readonly createdAfter?: string;
122
+ readonly createdBefore?: string;
123
+ };
124
+ readonly limit?: number;
125
+ readonly cursor?: string;
126
+ readonly order?: "newest" | "oldest";
127
+ readonly includeOrphaned?: boolean;
128
+ readonly includeTombstoned?: boolean;
129
+ }
130
+ export interface ThumbnailUploadInput extends AssetUploadInput {
131
+ /** Long-edge sizes to produce (e.g. [64, 256]). */
132
+ readonly sizes: readonly number[];
133
+ /**
134
+ * Downscaler. The SDK does NOT bundle an image library; callers pass
135
+ * a function that takes the original bytes and a target size and
136
+ * returns downscaled bytes. Typical implementations: `pica` in the
137
+ * browser, `sharp` in Node.
138
+ */
139
+ readonly downscale: (bytes: Uint8Array, targetLongEdge: number) => Promise<Uint8Array>;
140
+ }
141
+ export interface ThumbnailUploadResult {
142
+ readonly primary: AssetUploadResult;
143
+ readonly thumbnails: readonly {
144
+ size: number;
145
+ result: AssetUploadResult;
146
+ }[];
147
+ }
148
+ /**
149
+ * Maps an attaching context to the set of recipients whose wraps the
150
+ * AMK must carry. The v0.1 default returns the subject's own X25519
151
+ * sphere key derived from the SDK's seed.
152
+ *
153
+ * v0.2 will introduce an Ethos-aware resolver that inspects the
154
+ * current manifest's `zones.<z>.cipher.wraps[]` (or, in v0.3, the
155
+ * per-section wraps) to mirror grantees on attached assets.
156
+ */
157
+ export interface RecipientResolver {
158
+ resolve(input: {
159
+ subjectDid: string;
160
+ context: AttachedContext | undefined;
161
+ }): Promise<RecipientSet>;
162
+ }
163
+ export interface RecipientSet {
164
+ readonly recipients: ReadonlyArray<{
165
+ readonly didUrl: string;
166
+ readonly x25519PublicKey: Uint8Array;
167
+ }>;
168
+ }
169
+ export declare function createAssetsClient(args: CreateAssetsClientArgs): AssetsClient;
170
+ export declare class AssetsClient {
171
+ #private;
172
+ constructor(args: CreateAssetsClientArgs);
173
+ upload(input: AssetUploadInput): Promise<AssetUploadResult>;
174
+ /**
175
+ * Upload a primary asset plus one or more thumbnails (downscaled
176
+ * client-side). Convenience method for the Deep avatar use case and
177
+ * any UI that displays the same asset at multiple resolutions.
178
+ *
179
+ * The thumbnails are attached to the same context as the primary
180
+ * and uploaded in parallel. They carry the {@link AssetReference}
181
+ * role `"thumbnail"` when referenced from a section (see
182
+ * spec/assets/03-asset-descriptors.md §3.2.3).
183
+ */
184
+ uploadWithThumbnails(input: ThumbnailUploadInput): Promise<ThumbnailUploadResult>;
185
+ fetch(urn: string): Promise<AssetFetchResult>;
186
+ head(urn: string): Promise<AssetMetadata>;
187
+ list(opts?: ListAssetsOpts): Promise<{
188
+ items: AssetBrief[];
189
+ nextCursor?: string;
190
+ }>;
191
+ ref(urn: string, reference: AssetReference): Promise<{
192
+ referenceCount: number;
193
+ gammaRef: string;
194
+ }>;
195
+ unref(urn: string, reference: AssetReference): Promise<{
196
+ referenceCount: number;
197
+ gammaRef: string;
198
+ }>;
199
+ listReferences(urn: string): Promise<AssetReference[]>;
200
+ delete(urn: string): Promise<{
201
+ tombstonedAt: string;
202
+ gammaRef: string;
203
+ }>;
204
+ /** Zero in-memory AMK cache. Useful at user logout. */
205
+ reset(): void;
206
+ }
207
+ //# sourceMappingURL=assets.d.ts.map