@aithos/sdk 0.1.0-alpha.52 → 0.1.0-alpha.53

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.
@@ -420,7 +420,7 @@ export declare class AithosAuth {
420
420
  * Choose `"root"`, `"circle"`, or `"self"` only if the receiving
421
421
  * server specifically expects one of those (rare).
422
422
  */
423
- readonly sphere?: "root" | "public" | "circle" | "self";
423
+ readonly sphere?: "root" | "public" | "circle" | "self" | "data";
424
424
  /**
425
425
  * Envelope lifetime in seconds. Default 60. Aithos servers cap
426
426
  * at 300; third-party servers may apply their own cap.
package/dist/src/auth.js CHANGED
@@ -192,8 +192,9 @@ export class AithosAuth {
192
192
  if (sphere !== "root" &&
193
193
  sphere !== "public" &&
194
194
  sphere !== "circle" &&
195
- sphere !== "self") {
196
- throw new AithosSDKError("auth_invalid_sphere", `signEnvelope: invalid sphere "${sphere}". Expected one of: root, public, circle, self.`);
195
+ sphere !== "self" &&
196
+ sphere !== "data") {
197
+ throw new AithosSDKError("auth_invalid_sphere", `signEnvelope: invalid sphere "${sphere}". Expected one of: root, public, circle, self, data.`);
197
198
  }
198
199
  const signer = this.#ownerSigners.signerForSphere(sphere);
199
200
  return signOwnerEnvelope({
@@ -412,6 +413,7 @@ export class AithosAuth {
412
413
  public: identity.public.seed,
413
414
  circle: identity.circle.seed,
414
415
  self: identity.self.seed,
416
+ ...(identity.data ? { data: identity.data.seed } : {}),
415
417
  },
416
418
  delegates: [],
417
419
  });
@@ -462,6 +464,7 @@ export class AithosAuth {
462
464
  public: bytesToHex(identity.public.seed),
463
465
  circle: bytesToHex(identity.circle.seed),
464
466
  self: bytesToHex(identity.self.seed),
467
+ ...(identity.data ? { data: bytesToHex(identity.data.seed) } : {}),
465
468
  },
466
469
  savedAt: new Date().toISOString(),
467
470
  });
@@ -833,6 +836,7 @@ export class AithosAuth {
833
836
  public: identity.public.seed,
834
837
  circle: identity.circle.seed,
835
838
  self: identity.self.seed,
839
+ ...(identity.data ? { data: identity.data.seed } : {}),
836
840
  },
837
841
  delegates: [],
838
842
  });
@@ -869,6 +873,7 @@ export class AithosAuth {
869
873
  public: bytesToHex(identity.public.seed),
870
874
  circle: bytesToHex(identity.circle.seed),
871
875
  self: bytesToHex(identity.self.seed),
876
+ ...(identity.data ? { data: bytesToHex(identity.data.seed) } : {}),
872
877
  },
873
878
  savedAt: new Date().toISOString(),
874
879
  });
@@ -17,6 +17,8 @@ export declare class OwnerSigners {
17
17
  readonly public: Signer;
18
18
  readonly circle: Signer;
19
19
  readonly self: Signer;
20
+ /** Optional dedicated #data sphere signer (absent on legacy owners). */
21
+ readonly data?: Signer;
20
22
  constructor(args: {
21
23
  did: string;
22
24
  handle: string;
@@ -25,6 +27,7 @@ export declare class OwnerSigners {
25
27
  public: Signer;
26
28
  circle: Signer;
27
29
  self: Signer;
30
+ data?: Signer;
28
31
  });
29
32
  /**
30
33
  * Build from a {@link BrowserIdentity}. The seeds inside `identity`
@@ -59,7 +62,7 @@ export declare class OwnerSigners {
59
62
  * have the sphere as a string ("public" | "circle" | "self") rather
60
63
  * than a typed accessor.
61
64
  */
62
- signerForSphere(sphere: "root" | "public" | "circle" | "self"): Signer;
65
+ signerForSphere(sphere: "root" | "public" | "circle" | "self" | "data"): Signer;
63
66
  /**
64
67
  * Internal — project to a {@link StoredIdentity} for protocol-client
65
68
  * interop. Reads seed bytes via {@link RawSeedSigner._unsafeKeyPair}
@@ -71,7 +74,7 @@ export declare class OwnerSigners {
71
74
  * @internal
72
75
  */
73
76
  _unsafeStoredIdentity(): StoredIdentity;
74
- /** Zeroize all four private seeds. Idempotent. */
77
+ /** Zeroize all private seeds. Idempotent. */
75
78
  destroy(): void;
76
79
  get destroyed(): boolean;
77
80
  }
@@ -34,6 +34,8 @@ export class OwnerSigners {
34
34
  public;
35
35
  circle;
36
36
  self;
37
+ /** Optional dedicated #data sphere signer (absent on legacy owners). */
38
+ data;
37
39
  #destroyed = false;
38
40
  constructor(args) {
39
41
  this.did = args.did;
@@ -43,6 +45,7 @@ export class OwnerSigners {
43
45
  this.public = args.public;
44
46
  this.circle = args.circle;
45
47
  this.self = args.self;
48
+ this.data = args.data;
46
49
  }
47
50
  /**
48
51
  * Build from a {@link BrowserIdentity}. The seeds inside `identity`
@@ -59,6 +62,11 @@ export class OwnerSigners {
59
62
  public: new RawSeedSigner(identity.public.seed, identity.public.publicKey),
60
63
  circle: new RawSeedSigner(identity.circle.seed, identity.circle.publicKey),
61
64
  self: new RawSeedSigner(identity.self.seed, identity.self.publicKey),
65
+ ...(identity.data
66
+ ? {
67
+ data: new RawSeedSigner(identity.data.seed, identity.data.publicKey),
68
+ }
69
+ : {}),
62
70
  });
63
71
  }
64
72
  /**
@@ -120,6 +128,11 @@ export class OwnerSigners {
120
128
  return this.circle;
121
129
  case "self":
122
130
  return this.self;
131
+ case "data":
132
+ if (!this.data) {
133
+ throw new Error("OwnerSigners: this owner has no #data sphere (legacy account)");
134
+ }
135
+ return this.data;
123
136
  }
124
137
  }
125
138
  /**
@@ -146,11 +159,18 @@ export class OwnerSigners {
146
159
  public: bytesToHexLocal(asRawSeed(this.public, "public")._unsafeKeyPair().seed),
147
160
  circle: bytesToHexLocal(asRawSeed(this.circle, "circle")._unsafeKeyPair().seed),
148
161
  self: bytesToHexLocal(asRawSeed(this.self, "self")._unsafeKeyPair().seed),
162
+ // Preserve the optional #data seed on vault re-encryption — dropping it
163
+ // here would silently lose the data sphere on the next delegate-add.
164
+ ...(this.data
165
+ ? {
166
+ data: bytesToHexLocal(asRawSeed(this.data, "data")._unsafeKeyPair().seed),
167
+ }
168
+ : {}),
149
169
  },
150
170
  savedAt: new Date().toISOString(),
151
171
  };
152
172
  }
153
- /** Zeroize all four private seeds. Idempotent. */
173
+ /** Zeroize all private seeds. Idempotent. */
154
174
  destroy() {
155
175
  if (this.#destroyed)
156
176
  return;
@@ -158,6 +178,7 @@ export class OwnerSigners {
158
178
  this.public.destroy();
159
179
  this.circle.destroy();
160
180
  this.self.destroy();
181
+ this.data?.destroy();
161
182
  this.#destroyed = true;
162
183
  }
163
184
  get destroyed() {
@@ -8,6 +8,8 @@ export interface ParsedRecoveryFile {
8
8
  readonly public: string;
9
9
  readonly circle: string;
10
10
  readonly self: string;
11
+ /** Optional dedicated #data sphere seed (absent on legacy recovery files). */
12
+ readonly data?: string;
11
13
  };
12
14
  }
13
15
  /**
@@ -44,6 +44,11 @@ export function parseRecoveryFile(text) {
44
44
  throw bad(`seeds_hex.${k}: expected 64-char lowercase hex`);
45
45
  }
46
46
  }
47
+ // #data is optional (absent on legacy recovery files); validate when present.
48
+ const dataSeed = seeds["data"];
49
+ if (dataSeed !== undefined && (typeof dataSeed !== "string" || !HEX_64.test(dataSeed))) {
50
+ throw bad("seeds_hex.data: expected 64-char lowercase hex");
51
+ }
47
52
  return {
48
53
  handle,
49
54
  displayName,
@@ -53,6 +58,7 @@ export function parseRecoveryFile(text) {
53
58
  public: seeds["public"],
54
59
  circle: seeds["circle"],
55
60
  self: seeds["self"],
61
+ ...(typeof dataSeed === "string" ? { data: dataSeed } : {}),
56
62
  },
57
63
  };
58
64
  }
@@ -78,6 +84,7 @@ export function serializeRecoveryFile(identity) {
78
84
  public: bytesToHex(identity.public.seed),
79
85
  circle: bytesToHex(identity.circle.seed),
80
86
  self: bytesToHex(identity.self.seed),
87
+ ...(identity.data ? { data: bytesToHex(identity.data.seed) } : {}),
81
88
  },
82
89
  saved_at: new Date().toISOString(),
83
90
  };
@@ -19,6 +19,12 @@ export interface StoredOwnerKeys {
19
19
  readonly public: string;
20
20
  readonly circle: string;
21
21
  readonly self: string;
22
+ /**
23
+ * Optional dedicated #data sphere seed (spec/data/02-key-hierarchy.md).
24
+ * Present on owners created since the #data sphere landed; absent on legacy
25
+ * owners (which sign data/asset PDS ops under #root).
26
+ */
27
+ readonly data?: string;
22
28
  };
23
29
  /** ISO-8601 timestamp of the original save. Informational. */
24
30
  readonly savedAt: string;
@@ -213,6 +213,12 @@ function validOwnerOrNull(v) {
213
213
  if (s[k].length !== 64)
214
214
  return null;
215
215
  }
216
+ // #data is optional (absent on legacy owners); validate only when present.
217
+ if (s["data"] !== undefined) {
218
+ if (typeof s["data"] !== "string" || s["data"].length !== 64) {
219
+ return null;
220
+ }
221
+ }
216
222
  if (typeof o["savedAt"] !== "string")
217
223
  return null;
218
224
  return o;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data-sphere.test.d.ts.map
@@ -0,0 +1,57 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 Mathieu Colla
3
+ // Tests for the optional #data sphere through the SDK's owner key material:
4
+ // the recovery file and OwnerSigners (vault re-encryption projection).
5
+ // Mirrors the protocol-core / protocol-client data-sphere tests.
6
+ import { strict as assert } from "node:assert";
7
+ import { describe, it } from "node:test";
8
+ import { createBrowserIdentity } from "@aithos/protocol-client";
9
+ import { OwnerSigners } from "../src/internal/owner-signers.js";
10
+ import { serializeRecoveryFile, parseRecoveryFile, } from "../src/internal/recovery-file.js";
11
+ describe("#data sphere — recovery file", () => {
12
+ it("round-trips the #data seed for a fresh (eager) identity", () => {
13
+ const id = createBrowserIdentity("alice", "Alice");
14
+ assert.ok(id.data, "createBrowserIdentity should be eager about #data");
15
+ const { text } = serializeRecoveryFile(id);
16
+ const parsed = parseRecoveryFile(text);
17
+ assert.equal(typeof parsed.seedsHex.data, "string");
18
+ assert.equal(parsed.seedsHex.data.length, 64);
19
+ });
20
+ it("legacy identity without #data serializes/parses without a data seed", () => {
21
+ const id = { ...createBrowserIdentity("dave", "Dave"), data: undefined };
22
+ const { text } = serializeRecoveryFile(id);
23
+ const parsed = parseRecoveryFile(text);
24
+ assert.equal(parsed.seedsHex.data, undefined);
25
+ });
26
+ });
27
+ describe("#data sphere — OwnerSigners", () => {
28
+ it("preserves the #data seed on the vault re-encryption projection", () => {
29
+ const id = createBrowserIdentity("bob", "Bob");
30
+ const signers = OwnerSigners.fromBrowserIdentity(id);
31
+ try {
32
+ // _unsafeStoredIdentity() is what the vault re-encryption path reads —
33
+ // dropping #data here would silently lose the sphere on delegate-add.
34
+ const projected = signers._unsafeStoredIdentity();
35
+ assert.ok(projected.seeds.data, "#data seed must survive projection");
36
+ // signerForSphere exposes the #data signer.
37
+ assert.ok(signers.data, "OwnerSigners.data signer should be present");
38
+ assert.equal(signers.signerForSphere("data"), signers.data);
39
+ }
40
+ finally {
41
+ signers.destroy();
42
+ }
43
+ });
44
+ it("legacy owner (no #data) → no data signer, signerForSphere('data') throws", () => {
45
+ const id = { ...createBrowserIdentity("erin", "Erin"), data: undefined };
46
+ const signers = OwnerSigners.fromBrowserIdentity(id);
47
+ try {
48
+ assert.equal(signers.data, undefined);
49
+ assert.equal(signers._unsafeStoredIdentity().seeds.data, undefined);
50
+ assert.throws(() => signers.signerForSphere("data"), /no #data sphere/);
51
+ }
52
+ finally {
53
+ signers.destroy();
54
+ }
55
+ });
56
+ });
57
+ //# sourceMappingURL=data-sphere.test.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aithos/sdk",
3
- "version": "0.1.0-alpha.52",
3
+ "version": "0.1.0-alpha.53",
4
4
  "description": "Aithos SDK — high-level TypeScript developer kit for building agentic apps on the Aithos protocol. Wraps @aithos/protocol-client and exposes the Aithos compute proxy and wallet (Stripe top-up) endpoints.",
5
5
  "keywords": [
6
6
  "aithos",
@@ -57,7 +57,7 @@
57
57
  },
58
58
  "peerDependencies": {
59
59
  "@aithos/assets-crypto": ">=0.1.0-alpha.1 <0.2.0",
60
- "@aithos/protocol-client": "^0.1.0-alpha.18",
60
+ "@aithos/protocol-client": "^0.1.0-alpha.19",
61
61
  "@aithos/protocol-core": ">=0.6.5 <0.7.0",
62
62
  "react": "^18.0.0 || ^19.0.0"
63
63
  },
@@ -71,7 +71,7 @@
71
71
  },
72
72
  "devDependencies": {
73
73
  "@aithos/assets-crypto": "^0.1.0-alpha.1",
74
- "@aithos/protocol-client": "^0.1.0-alpha.18",
74
+ "@aithos/protocol-client": "^0.1.0-alpha.19",
75
75
  "@aithos/protocol-core": ">=0.6.5 <0.7.0",
76
76
  "@types/node": "^24.12.2",
77
77
  "fake-indexeddb": "^6.2.5",