@aithos/sdk 0.1.0-alpha.4 → 0.1.0-alpha.6
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 +45 -0
- package/dist/src/auth.d.ts +103 -134
- package/dist/src/auth.js +532 -157
- package/dist/src/compute.d.ts +8 -6
- package/dist/src/compute.js +19 -11
- package/dist/src/ethos.d.ts +117 -1
- package/dist/src/ethos.js +417 -16
- package/dist/src/index.d.ts +8 -5
- package/dist/src/index.js +18 -6
- package/dist/src/internal/delegate-bundle.d.ts +18 -0
- package/dist/src/internal/delegate-bundle.js +89 -0
- package/dist/src/internal/delegate-state.d.ts +45 -0
- package/dist/src/internal/delegate-state.js +120 -0
- package/dist/src/internal/owner-signers.d.ts +78 -0
- package/dist/src/internal/owner-signers.js +179 -0
- package/dist/src/internal/protocol-client-bridge.d.ts +8 -0
- package/dist/src/internal/protocol-client-bridge.js +20 -0
- package/dist/src/internal/recovery-file.d.ts +29 -0
- package/dist/src/internal/recovery-file.js +98 -0
- package/dist/src/internal/signer.d.ts +59 -0
- package/dist/src/internal/signer.js +86 -0
- package/dist/src/key-store.d.ts +128 -0
- package/dist/src/key-store.js +244 -0
- package/dist/src/mandates.d.ts +151 -1
- package/dist/src/mandates.js +285 -8
- package/dist/src/sdk.d.ts +36 -3
- package/dist/src/sdk.js +27 -23
- package/dist/src/wallet.d.ts +4 -6
- package/dist/src/wallet.js +18 -8
- package/dist/test/auth-j3.test.d.ts +2 -0
- package/dist/test/auth-j3.test.js +360 -0
- package/dist/test/compute.test.js +22 -11
- package/dist/test/ethos.test.d.ts +2 -0
- package/dist/test/ethos.test.js +219 -0
- package/dist/test/key-store.test.d.ts +2 -0
- package/dist/test/key-store.test.js +161 -0
- package/dist/test/mandates-compute.test.d.ts +2 -0
- package/dist/test/mandates-compute.test.js +256 -0
- package/dist/test/mandates.test.d.ts +2 -0
- package/dist/test/mandates.test.js +93 -0
- package/dist/test/sdk.test.js +64 -30
- package/dist/test/signer.test.d.ts +2 -0
- package/dist/test/signer.test.js +117 -0
- package/dist/test/wallet.test.js +20 -9
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -55,6 +55,51 @@ 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
|
+
|
|
58
103
|
## What lives where
|
|
59
104
|
|
|
60
105
|
| Namespace | Purpose |
|
package/dist/src/auth.d.ts
CHANGED
|
@@ -1,197 +1,166 @@
|
|
|
1
1
|
import { type AithosSessionStore } from "./session-store.js";
|
|
2
|
+
import { type AithosKeyStore } from "./key-store.js";
|
|
3
|
+
import { DelegateActor } from "./internal/delegate-state.js";
|
|
4
|
+
import { OwnerSigners } from "./internal/owner-signers.js";
|
|
2
5
|
/** Default URL of the Aithos auth backend. */
|
|
3
6
|
export declare const DEFAULT_AUTH_BASE_URL = "https://auth.aithos.be";
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
*/
|
|
7
|
+
/** Default URL of the Aithos primitives API (publish_identity, publish_ethos_edition, etc.). */
|
|
8
|
+
export declare const DEFAULT_API_BASE_URL = "https://api.aithos.be";
|
|
7
9
|
export interface AithosAuthConfig {
|
|
8
|
-
/**
|
|
9
|
-
* Base URL of the Aithos auth backend. Defaults to
|
|
10
|
-
* {@link DEFAULT_AUTH_BASE_URL}. Override for staging or self-hosted
|
|
11
|
-
* deployments.
|
|
12
|
-
*/
|
|
13
10
|
readonly authBaseUrl?: string;
|
|
14
11
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* Base URL of the Aithos primitives API (`api.aithos.be`). Used by
|
|
13
|
+
* {@link AithosAuth.signUp} to bootstrap the user's Ethos via
|
|
14
|
+
* `aithos.publish_identity` after the auth account is created. Override
|
|
15
|
+
* for staging or self-hosted deployments. Defaults to
|
|
16
|
+
* {@link DEFAULT_API_BASE_URL}.
|
|
17
17
|
*/
|
|
18
|
+
readonly apiBaseUrl?: string;
|
|
18
19
|
readonly fetch?: typeof fetch;
|
|
19
|
-
/**
|
|
20
|
-
* Optional `window`-like object. Defaults to `globalThis.window` when
|
|
21
|
-
* available. Provided so node-side tests can assert redirect URLs without
|
|
22
|
-
* shimming jsdom.
|
|
23
|
-
*/
|
|
24
20
|
readonly window?: Pick<Window, "location" | "history">;
|
|
25
|
-
/**
|
|
26
|
-
* Pluggable session storage. Defaults to {@link sessionStorageStore}
|
|
27
|
-
* in browser environments, {@link noopStore} elsewhere. See
|
|
28
|
-
* `./session-store.ts` for built-in alternatives or to roll your own.
|
|
29
|
-
*/
|
|
21
|
+
/** Pluggable JWT-session storage. Defaults to {@link defaultSessionStore}. */
|
|
30
22
|
readonly sessionStore?: AithosSessionStore;
|
|
23
|
+
/** Pluggable key persistence. Defaults to {@link defaultKeyStore}. */
|
|
24
|
+
readonly keyStore?: AithosKeyStore;
|
|
31
25
|
}
|
|
32
26
|
/**
|
|
33
|
-
* Active Aithos session.
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* register/verify payloads ; field names are kept snake_case to match
|
|
38
|
-
* the backend.
|
|
27
|
+
* Active Aithos session. Returned by JWT-backed entry points
|
|
28
|
+
* (`signIn`, `signUp`, `handleCallback`). Recovery-file and mandate
|
|
29
|
+
* sign-ins do NOT return an `AithosSession` — they yield the lighter
|
|
30
|
+
* {@link OwnerInfo} / {@link DelegateInfo}.
|
|
39
31
|
*/
|
|
40
32
|
export interface AithosSession {
|
|
41
|
-
/** HS256 JWT — send in `Authorization: Bearer <session>` to auth/* and
|
|
42
|
-
* app endpoints that consume it. */
|
|
43
33
|
readonly session: string;
|
|
44
|
-
/** JWT expiry, Unix seconds. */
|
|
45
34
|
readonly exp: number;
|
|
46
|
-
/** Aithos DID — `did:aithos:z…`. Stable across all the user's devices. */
|
|
47
35
|
readonly did: string;
|
|
48
|
-
/** User-visible handle (rendered as `@handle`). */
|
|
49
36
|
readonly handle: string;
|
|
50
|
-
/** Encrypted vault blob, base64. Empty string on first Google sign-in. */
|
|
51
37
|
readonly blob_b64: string;
|
|
52
|
-
/** AES-GCM nonce for the blob, base64 (12 bytes). Empty on first Google sign-in. */
|
|
53
38
|
readonly blob_nonce_b64: string;
|
|
54
|
-
/** Monotonic blob version. 0 on first Google sign-in. */
|
|
55
39
|
readonly blob_version: number;
|
|
56
|
-
/** 32-byte vault key, base64. Decrypts {@link blob_b64} via AES-GCM-256.
|
|
57
|
-
* Returned by Google SSO ; absent (empty string) for password sign-in
|
|
58
|
-
* where the key stays in browser memory only. */
|
|
59
40
|
readonly enc_key_b64: string;
|
|
60
|
-
/** True the first time this user signs in (Google flow only). */
|
|
61
41
|
readonly is_first_login: boolean;
|
|
62
42
|
}
|
|
63
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Public information about the loaded owner identity. Available after
|
|
45
|
+
* any owner-side sign-in (password, Google, recovery), regardless of
|
|
46
|
+
* whether a JWT is also present.
|
|
47
|
+
*/
|
|
48
|
+
export interface OwnerInfo {
|
|
49
|
+
readonly did: string;
|
|
50
|
+
readonly handle: string;
|
|
51
|
+
readonly displayName: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Public information about a delegate session held by the SDK. Returned
|
|
55
|
+
* by `importMandate` and `getDelegates`.
|
|
56
|
+
*/
|
|
57
|
+
export interface DelegateInfo {
|
|
58
|
+
readonly mandateId: string;
|
|
59
|
+
readonly subjectDid: string;
|
|
60
|
+
readonly granteeId: string;
|
|
61
|
+
readonly scopes: readonly string[];
|
|
62
|
+
/** ISO-8601, or null when the mandate has no `not_after`. */
|
|
63
|
+
readonly expiresAt: string | null;
|
|
64
|
+
readonly label?: string;
|
|
65
|
+
}
|
|
64
66
|
export interface SignInWithGoogleOptions {
|
|
65
|
-
/**
|
|
66
|
-
* Opaque deep-link state preserved across the OAuth round-trip and
|
|
67
|
-
* surfaced back to the app via `?app_state=…` on the callback URL. Use
|
|
68
|
-
* to remember "the user clicked sign-in from /settings/billing" so you
|
|
69
|
-
* can restore that route after the redirect chain.
|
|
70
|
-
*
|
|
71
|
-
* Maximum 1024 characters.
|
|
72
|
-
*/
|
|
73
67
|
readonly appState?: string;
|
|
74
68
|
}
|
|
75
|
-
/** Options for {@link AithosAuth.signIn}. */
|
|
76
69
|
export interface SignInInput {
|
|
77
70
|
readonly email: string;
|
|
78
71
|
readonly password: string;
|
|
79
72
|
}
|
|
80
|
-
/** Options for {@link AithosAuth.signUp}. */
|
|
81
73
|
export interface SignUpInput {
|
|
82
74
|
readonly email: string;
|
|
83
75
|
readonly password: string;
|
|
84
|
-
/** Aithos handle (the @-name). 1–63 alphanumeric chars + `_`/`-`. */
|
|
85
76
|
readonly handle: string;
|
|
86
|
-
/** Optional human-readable name. Defaults to the handle. */
|
|
87
77
|
readonly displayName?: string;
|
|
88
78
|
}
|
|
89
|
-
/** Result of {@link AithosAuth.signUp}. */
|
|
90
79
|
export interface SignUpResult {
|
|
91
80
|
readonly session: AithosSession;
|
|
92
|
-
/**
|
|
93
|
-
* Recovery file containing the user's seed material, plaintext at the
|
|
94
|
-
* V1 spec ; the app should offer this to the user as a download. Lose
|
|
95
|
-
* this file AND forget the password = lose the ethos.
|
|
96
|
-
*
|
|
97
|
-
* Format : JSON, content-type `application/json`. Filename suggestion
|
|
98
|
-
* exposed as {@link recoveryFilename} for direct use with `<a download>`.
|
|
99
|
-
*/
|
|
100
81
|
readonly recoveryFile: Blob;
|
|
101
|
-
/** Suggested filename for {@link recoveryFile}. */
|
|
102
82
|
readonly recoveryFilename: string;
|
|
103
83
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
*/
|
|
84
|
+
export interface SignInWithRecoveryInput {
|
|
85
|
+
/** Recovery file as a Blob (browser File input) or already-decoded JSON string. */
|
|
86
|
+
readonly file: Blob | string;
|
|
87
|
+
}
|
|
88
|
+
export interface ImportMandateInput {
|
|
89
|
+
/** Delegate bundle as a Blob or already-decoded JSON string. */
|
|
90
|
+
readonly bundle: Blob | string;
|
|
91
|
+
}
|
|
113
92
|
export declare class AithosAuth {
|
|
114
|
-
|
|
93
|
+
#private;
|
|
115
94
|
readonly authBaseUrl: string;
|
|
116
|
-
|
|
117
|
-
private readonly win;
|
|
118
|
-
private readonly store;
|
|
95
|
+
readonly apiBaseUrl: string;
|
|
119
96
|
constructor(config?: AithosAuthConfig);
|
|
120
97
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
98
|
+
* Reload signing material and JWT session from the configured stores.
|
|
99
|
+
* Must be called once at app boot before relying on
|
|
100
|
+
* {@link getCurrentSession} / {@link getOwnerInfo} / {@link canSignAsOwner}
|
|
101
|
+
* — until then they reflect only what's been done in-memory in the
|
|
102
|
+
* current tab.
|
|
123
103
|
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* doesn't release the vault key over the wire (it's derived locally
|
|
130
|
-
* but discarded after the call). Apps that need the seeds — most
|
|
131
|
-
* apps don't — should use the (forthcoming) `loadEthos` helper
|
|
132
|
-
* separately with the password still in hand.
|
|
133
|
-
*
|
|
134
|
-
* Persists the session in the configured store before returning.
|
|
104
|
+
* Strict consistency: if the JWT and the stored owner disagree about
|
|
105
|
+
* who's signed in, both are wiped and the user re-auths. JWT-less
|
|
106
|
+
* owner state (loaded from keyStore but no JWT) is a valid resumed
|
|
107
|
+
* state — the user signed in via recovery or imported a mandate at
|
|
108
|
+
* some earlier moment and never went through the JWT flow.
|
|
135
109
|
*/
|
|
136
|
-
|
|
110
|
+
resume(): Promise<void>;
|
|
111
|
+
/** JWT-backed session. Null when signed in via recovery / mandate / not at all. */
|
|
112
|
+
getCurrentSession(): AithosSession | null;
|
|
113
|
+
/** Loaded owner identity. Independent of JWT presence. */
|
|
114
|
+
getOwnerInfo(): OwnerInfo | null;
|
|
115
|
+
getDelegates(): readonly DelegateInfo[];
|
|
116
|
+
canSignAsOwner(): boolean;
|
|
117
|
+
canSignAsDelegateFor(did: string): boolean;
|
|
137
118
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* 2. Build the recovery file (plaintext JSON, the user must save it)
|
|
142
|
-
* 3. Derive auth_key + enc_key from the password (Argon2id, fresh salts)
|
|
143
|
-
* 4. Encrypt the seeds in a vault blob (AES-GCM-256)
|
|
144
|
-
* 5. POST /auth/register with everything → JWT
|
|
145
|
-
* 6. Persist the session and return it + the recovery Blob
|
|
119
|
+
* Internal accessor used by sibling SDK namespaces (compute, wallet,
|
|
120
|
+
* ethos) when they need to sign on behalf of the owner. Returns null
|
|
121
|
+
* if no owner is loaded.
|
|
146
122
|
*
|
|
147
|
-
*
|
|
148
|
-
* profile on `app.aithos.be` won't appear until they (or another app)
|
|
149
|
-
* publishes their first edition. This matches `aithos/app`'s design,
|
|
150
|
-
* where the vault is the source of truth for keys and the published
|
|
151
|
-
* edition is a separate concern.
|
|
123
|
+
* @internal
|
|
152
124
|
*/
|
|
153
|
-
|
|
125
|
+
_getOwnerSigners(): OwnerSigners | null;
|
|
154
126
|
/**
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
* browsers block top-level navigation triggered from idle code.
|
|
158
|
-
*
|
|
159
|
-
* Does not return : navigation tears the JS context down. The `never`
|
|
160
|
-
* return type tells callers any code after the call is unreachable.
|
|
127
|
+
* Internal accessor — looks up an active delegate by mandate id.
|
|
128
|
+
* @internal
|
|
161
129
|
*/
|
|
162
|
-
|
|
130
|
+
_getDelegateActor(mandateId: string): DelegateActor | undefined;
|
|
163
131
|
/**
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
* (which would 410 anyway).
|
|
169
|
-
*
|
|
170
|
-
* Returns `null` when there's no code in the URL — safe to call on every
|
|
171
|
-
* page load. Throws {@link AithosSDKError} on backend errors or when
|
|
172
|
-
* the URL carries `aithos_error=…`.
|
|
132
|
+
* Internal accessor — finds the first active delegate whose subject
|
|
133
|
+
* matches `did`. Used by `sdk.ethos.of(did)` when the user holds a
|
|
134
|
+
* mandate for that subject.
|
|
135
|
+
* @internal
|
|
173
136
|
*/
|
|
174
|
-
|
|
137
|
+
_findDelegateForSubject(did: string): DelegateActor | undefined;
|
|
138
|
+
signIn(input: SignInInput): Promise<AithosSession>;
|
|
139
|
+
signUp(input: SignUpInput): Promise<SignUpResult>;
|
|
175
140
|
/**
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
141
|
+
* Sign in by uploading a recovery file. Hydrates the owner signers
|
|
142
|
+
* locally — no JWT is obtained on this path because the recovery
|
|
143
|
+
* file alone doesn't authenticate against the auth backend (no
|
|
144
|
+
* password, no Google session). Apps that need compute/wallet
|
|
145
|
+
* access should follow up with an email+password sign-in or with
|
|
146
|
+
* Google SSO.
|
|
179
147
|
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
148
|
+
* The recovery file is ALWAYS the file produced by `signUp` (or the
|
|
149
|
+
* equivalent one emitted by `protocol-client`'s `runOnboarding`).
|
|
150
|
+
* Both shapes are accepted.
|
|
182
151
|
*/
|
|
183
|
-
|
|
152
|
+
signInWithRecovery(input: SignInWithRecoveryInput): Promise<OwnerInfo>;
|
|
184
153
|
/**
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
|
|
189
|
-
getCurrentSession(): AithosSession | null;
|
|
190
|
-
/**
|
|
191
|
-
* Stateless sign-out — the Aithos backend doesn't track sessions, so
|
|
192
|
-
* there's nothing to revoke server-side ; this method clears the
|
|
193
|
-
* configured session store and resolves.
|
|
154
|
+
* Import a delegate bundle (`.aithos-delegate.json`). Works in any
|
|
155
|
+
* state: with no owner loaded (delegate-only session), or alongside
|
|
156
|
+
* an existing owner (the user holds mandates for other people's
|
|
157
|
+
* ethoses while also being an owner themselves).
|
|
194
158
|
*/
|
|
159
|
+
importMandate(input: ImportMandateInput): Promise<DelegateInfo>;
|
|
160
|
+
removeMandate(mandateId: string): Promise<void>;
|
|
161
|
+
signInWithGoogle(opts?: SignInWithGoogleOptions): never;
|
|
162
|
+
handleCallback(): Promise<AithosSession | null>;
|
|
163
|
+
exchange(aithosCode: string): Promise<AithosSession>;
|
|
195
164
|
signOut(): Promise<void>;
|
|
196
165
|
}
|
|
197
166
|
//# sourceMappingURL=auth.d.ts.map
|