@aithos/sdk 0.1.0-alpha.4 → 0.1.0-alpha.41
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 +211 -7
- package/dist/src/apps.d.ts +155 -0
- package/dist/src/apps.js +288 -0
- package/dist/src/assets.d.ts +207 -0
- package/dist/src/assets.js +533 -0
- package/dist/src/auth-api.d.ts +138 -0
- package/dist/src/auth-api.js +168 -0
- package/dist/src/auth.d.ts +536 -119
- package/dist/src/auth.js +1207 -152
- package/dist/src/compute.d.ts +251 -9
- package/dist/src/compute.js +293 -16
- package/dist/src/data-schema-contacts-v1.d.ts +14 -0
- package/dist/src/data-schema-contacts-v1.js +28 -0
- package/dist/src/data.d.ts +153 -0
- package/dist/src/data.js +670 -0
- package/dist/src/endpoints.d.ts +9 -0
- package/dist/src/endpoints.js +5 -0
- package/dist/src/ethos.d.ts +202 -1
- package/dist/src/ethos.js +821 -16
- package/dist/src/index.d.ts +18 -6
- package/dist/src/index.js +39 -6
- package/dist/src/internal/delegate-bundle.d.ts +18 -0
- package/dist/src/internal/delegate-bundle.js +94 -0
- package/dist/src/internal/delegate-state.d.ts +45 -0
- package/dist/src/internal/delegate-state.js +120 -0
- package/dist/src/internal/envelope.d.ts +77 -0
- package/dist/src/internal/envelope.js +154 -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 +163 -1
- package/dist/src/mandates.js +286 -8
- package/dist/src/react/AithosAsset.d.ts +66 -0
- package/dist/src/react/AithosAsset.js +67 -0
- package/dist/src/react/context.d.ts +29 -0
- package/dist/src/react/context.js +31 -0
- package/dist/src/react/index.d.ts +28 -0
- package/dist/src/react/index.js +30 -0
- package/dist/src/react/use-aithos-asset.d.ts +39 -0
- package/dist/src/react/use-aithos-asset.js +118 -0
- package/dist/src/sdk.d.ts +46 -3
- package/dist/src/sdk.js +49 -23
- package/dist/src/wallet.d.ts +4 -6
- package/dist/src/wallet.js +18 -8
- package/dist/src/web.d.ts +279 -0
- package/dist/src/web.js +186 -0
- package/dist/test/auth-j3.test.d.ts +2 -0
- package/dist/test/auth-j3.test.js +391 -0
- package/dist/test/compute-delegate-path.test.d.ts +2 -0
- package/dist/test/compute-delegate-path.test.js +183 -0
- package/dist/test/compute.test.js +26 -11
- package/dist/test/endpoints.test.js +20 -1
- package/dist/test/envelope.test.d.ts +2 -0
- package/dist/test/envelope.test.js +318 -0
- package/dist/test/ethos-first-edition.test.d.ts +2 -0
- package/dist/test/ethos-first-edition.test.js +248 -0
- 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 +70 -30
- package/dist/test/signer.test.d.ts +2 -0
- package/dist/test/signer.test.js +117 -0
- package/dist/test/signup-bootstrap.test.d.ts +2 -0
- package/dist/test/signup-bootstrap.test.js +311 -0
- package/dist/test/wallet.test.js +20 -9
- package/dist/test/web.test.d.ts +2 -0
- package/dist/test/web.test.js +270 -0
- package/package.json +18 -3
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type BlobPlaintext, type BrowserIdentity, type StoredIdentity } from "@aithos/protocol-client";
|
|
2
|
+
import type { StoredOwnerKeys } from "../key-store.js";
|
|
3
|
+
import { type Signer } from "./signer.js";
|
|
4
|
+
/**
|
|
5
|
+
* The four signing capabilities of an authenticated owner.
|
|
6
|
+
*
|
|
7
|
+
* The `did`, `handle`, `displayName` fields are public metadata.
|
|
8
|
+
* The four signers expose only `publicKey` + `sign` — never the
|
|
9
|
+
* underlying seed bytes.
|
|
10
|
+
*/
|
|
11
|
+
export declare class OwnerSigners {
|
|
12
|
+
#private;
|
|
13
|
+
readonly did: string;
|
|
14
|
+
readonly handle: string;
|
|
15
|
+
readonly displayName: string;
|
|
16
|
+
readonly root: Signer;
|
|
17
|
+
readonly public: Signer;
|
|
18
|
+
readonly circle: Signer;
|
|
19
|
+
readonly self: Signer;
|
|
20
|
+
constructor(args: {
|
|
21
|
+
did: string;
|
|
22
|
+
handle: string;
|
|
23
|
+
displayName: string;
|
|
24
|
+
root: Signer;
|
|
25
|
+
public: Signer;
|
|
26
|
+
circle: Signer;
|
|
27
|
+
self: Signer;
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* Build from a {@link BrowserIdentity}. The seeds inside `identity`
|
|
31
|
+
* are defensively copied into fresh signers — mutating the
|
|
32
|
+
* BrowserIdentity afterwards does not affect the signers, and the
|
|
33
|
+
* caller may zeroize the original identity immediately.
|
|
34
|
+
*/
|
|
35
|
+
static fromBrowserIdentity(identity: BrowserIdentity): OwnerSigners;
|
|
36
|
+
/**
|
|
37
|
+
* Build from a {@link StoredIdentity} — the persisted shape used by
|
|
38
|
+
* extension-kit's IndexedDbKeystore (hex-encoded seeds).
|
|
39
|
+
*
|
|
40
|
+
* Internally calls `browserIdentityFromStored` to derive
|
|
41
|
+
* `publicKey`s, then wraps. The caller may discard the StoredIdentity
|
|
42
|
+
* afterwards.
|
|
43
|
+
*/
|
|
44
|
+
static fromStoredIdentity(stored: StoredIdentity): OwnerSigners;
|
|
45
|
+
/**
|
|
46
|
+
* Build from the SDK's own {@link StoredOwnerKeys} shape — what the
|
|
47
|
+
* KeyStore round-trips for owner-side resume.
|
|
48
|
+
*/
|
|
49
|
+
static fromStoredOwnerKeys(stored: StoredOwnerKeys): OwnerSigners;
|
|
50
|
+
/**
|
|
51
|
+
* Build from a {@link BlobPlaintext} — the parsed payload of the
|
|
52
|
+
* password-decrypted vault blob returned by `loginVerify`. Hex
|
|
53
|
+
* seeds are turned into KeyPairs eagerly; the plaintext input is
|
|
54
|
+
* not retained.
|
|
55
|
+
*/
|
|
56
|
+
static fromBlobPlaintext(plaintext: BlobPlaintext): OwnerSigners;
|
|
57
|
+
/**
|
|
58
|
+
* Resolve a sphere name to its signer. Convenience for callers that
|
|
59
|
+
* have the sphere as a string ("public" | "circle" | "self") rather
|
|
60
|
+
* than a typed accessor.
|
|
61
|
+
*/
|
|
62
|
+
signerForSphere(sphere: "root" | "public" | "circle" | "self"): Signer;
|
|
63
|
+
/**
|
|
64
|
+
* Internal — project to a {@link StoredIdentity} for protocol-client
|
|
65
|
+
* interop. Reads seed bytes via {@link RawSeedSigner._unsafeKeyPair}
|
|
66
|
+
* on each signer and hex-encodes them. Throws if any signer is not
|
|
67
|
+
* a RawSeedSigner (which can happen post-migration to SubtleSigner;
|
|
68
|
+
* at that point protocol-client must accept Signer-shaped objects
|
|
69
|
+
* directly and this method goes away).
|
|
70
|
+
*
|
|
71
|
+
* @internal
|
|
72
|
+
*/
|
|
73
|
+
_unsafeStoredIdentity(): StoredIdentity;
|
|
74
|
+
/** Zeroize all four private seeds. Idempotent. */
|
|
75
|
+
destroy(): void;
|
|
76
|
+
get destroyed(): boolean;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=owner-signers.d.ts.map
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 Mathieu Colla
|
|
3
|
+
// OwnerSigners — the four-keypair signing capability of an Aithos
|
|
4
|
+
// owner identity, behind the {@link Signer} interface.
|
|
5
|
+
//
|
|
6
|
+
// SDK-internal only — NOT exported from the package barrel.
|
|
7
|
+
//
|
|
8
|
+
// An owner identity has four key roles: `root` (controls the DID
|
|
9
|
+
// document), `public` (signs writes / envelopes for the public zone),
|
|
10
|
+
// `circle` (signs writes for the friends zone), `self` (signs writes
|
|
11
|
+
// for the private zone). All four are Ed25519. The X25519 keys used
|
|
12
|
+
// for HPKE zone encryption are deterministically derived from the
|
|
13
|
+
// Ed25519 seeds at use-site (see protocol-client/crypto/kex), so we
|
|
14
|
+
// only need to hold the Ed25519 signers here.
|
|
15
|
+
//
|
|
16
|
+
// This class is the in-memory home for those signers throughout an
|
|
17
|
+
// authenticated session. The auth namespace owns a single instance,
|
|
18
|
+
// hands references to the compute / wallet / ethos namespaces, and
|
|
19
|
+
// destroys it on `signOut`.
|
|
20
|
+
import { browserIdentityFromStored, } from "@aithos/protocol-client";
|
|
21
|
+
import { RawSeedSigner } from "./signer.js";
|
|
22
|
+
/**
|
|
23
|
+
* The four signing capabilities of an authenticated owner.
|
|
24
|
+
*
|
|
25
|
+
* The `did`, `handle`, `displayName` fields are public metadata.
|
|
26
|
+
* The four signers expose only `publicKey` + `sign` — never the
|
|
27
|
+
* underlying seed bytes.
|
|
28
|
+
*/
|
|
29
|
+
export class OwnerSigners {
|
|
30
|
+
did;
|
|
31
|
+
handle;
|
|
32
|
+
displayName;
|
|
33
|
+
root;
|
|
34
|
+
public;
|
|
35
|
+
circle;
|
|
36
|
+
self;
|
|
37
|
+
#destroyed = false;
|
|
38
|
+
constructor(args) {
|
|
39
|
+
this.did = args.did;
|
|
40
|
+
this.handle = args.handle;
|
|
41
|
+
this.displayName = args.displayName;
|
|
42
|
+
this.root = args.root;
|
|
43
|
+
this.public = args.public;
|
|
44
|
+
this.circle = args.circle;
|
|
45
|
+
this.self = args.self;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build from a {@link BrowserIdentity}. The seeds inside `identity`
|
|
49
|
+
* are defensively copied into fresh signers — mutating the
|
|
50
|
+
* BrowserIdentity afterwards does not affect the signers, and the
|
|
51
|
+
* caller may zeroize the original identity immediately.
|
|
52
|
+
*/
|
|
53
|
+
static fromBrowserIdentity(identity) {
|
|
54
|
+
return new OwnerSigners({
|
|
55
|
+
did: identity.did,
|
|
56
|
+
handle: identity.handle,
|
|
57
|
+
displayName: identity.displayName,
|
|
58
|
+
root: new RawSeedSigner(identity.root.seed, identity.root.publicKey),
|
|
59
|
+
public: new RawSeedSigner(identity.public.seed, identity.public.publicKey),
|
|
60
|
+
circle: new RawSeedSigner(identity.circle.seed, identity.circle.publicKey),
|
|
61
|
+
self: new RawSeedSigner(identity.self.seed, identity.self.publicKey),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Build from a {@link StoredIdentity} — the persisted shape used by
|
|
66
|
+
* extension-kit's IndexedDbKeystore (hex-encoded seeds).
|
|
67
|
+
*
|
|
68
|
+
* Internally calls `browserIdentityFromStored` to derive
|
|
69
|
+
* `publicKey`s, then wraps. The caller may discard the StoredIdentity
|
|
70
|
+
* afterwards.
|
|
71
|
+
*/
|
|
72
|
+
static fromStoredIdentity(stored) {
|
|
73
|
+
return OwnerSigners.fromBrowserIdentity(browserIdentityFromStored(stored));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Build from the SDK's own {@link StoredOwnerKeys} shape — what the
|
|
77
|
+
* KeyStore round-trips for owner-side resume.
|
|
78
|
+
*/
|
|
79
|
+
static fromStoredOwnerKeys(stored) {
|
|
80
|
+
return OwnerSigners.fromStoredIdentity({
|
|
81
|
+
version: "0.1.0",
|
|
82
|
+
handle: stored.handle,
|
|
83
|
+
displayName: stored.displayName,
|
|
84
|
+
did: stored.did,
|
|
85
|
+
seeds: stored.seedsHex,
|
|
86
|
+
savedAt: stored.savedAt,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Build from a {@link BlobPlaintext} — the parsed payload of the
|
|
91
|
+
* password-decrypted vault blob returned by `loginVerify`. Hex
|
|
92
|
+
* seeds are turned into KeyPairs eagerly; the plaintext input is
|
|
93
|
+
* not retained.
|
|
94
|
+
*/
|
|
95
|
+
static fromBlobPlaintext(plaintext) {
|
|
96
|
+
return OwnerSigners.fromStoredIdentity({
|
|
97
|
+
version: "0.1.0",
|
|
98
|
+
handle: plaintext.identity.handle,
|
|
99
|
+
displayName: plaintext.identity.displayName,
|
|
100
|
+
did: plaintext.identity.did,
|
|
101
|
+
seeds: plaintext.seeds,
|
|
102
|
+
savedAt: new Date().toISOString(),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Resolve a sphere name to its signer. Convenience for callers that
|
|
107
|
+
* have the sphere as a string ("public" | "circle" | "self") rather
|
|
108
|
+
* than a typed accessor.
|
|
109
|
+
*/
|
|
110
|
+
signerForSphere(sphere) {
|
|
111
|
+
if (this.#destroyed) {
|
|
112
|
+
throw new Error("OwnerSigners: cannot use destroyed signers");
|
|
113
|
+
}
|
|
114
|
+
switch (sphere) {
|
|
115
|
+
case "root":
|
|
116
|
+
return this.root;
|
|
117
|
+
case "public":
|
|
118
|
+
return this.public;
|
|
119
|
+
case "circle":
|
|
120
|
+
return this.circle;
|
|
121
|
+
case "self":
|
|
122
|
+
return this.self;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Internal — project to a {@link StoredIdentity} for protocol-client
|
|
127
|
+
* interop. Reads seed bytes via {@link RawSeedSigner._unsafeKeyPair}
|
|
128
|
+
* on each signer and hex-encodes them. Throws if any signer is not
|
|
129
|
+
* a RawSeedSigner (which can happen post-migration to SubtleSigner;
|
|
130
|
+
* at that point protocol-client must accept Signer-shaped objects
|
|
131
|
+
* directly and this method goes away).
|
|
132
|
+
*
|
|
133
|
+
* @internal
|
|
134
|
+
*/
|
|
135
|
+
_unsafeStoredIdentity() {
|
|
136
|
+
if (this.#destroyed) {
|
|
137
|
+
throw new Error("OwnerSigners: cannot project destroyed signers");
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
version: "0.1.0",
|
|
141
|
+
handle: this.handle,
|
|
142
|
+
displayName: this.displayName,
|
|
143
|
+
did: this.did,
|
|
144
|
+
seeds: {
|
|
145
|
+
root: bytesToHexLocal(asRawSeed(this.root, "root")._unsafeKeyPair().seed),
|
|
146
|
+
public: bytesToHexLocal(asRawSeed(this.public, "public")._unsafeKeyPair().seed),
|
|
147
|
+
circle: bytesToHexLocal(asRawSeed(this.circle, "circle")._unsafeKeyPair().seed),
|
|
148
|
+
self: bytesToHexLocal(asRawSeed(this.self, "self")._unsafeKeyPair().seed),
|
|
149
|
+
},
|
|
150
|
+
savedAt: new Date().toISOString(),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/** Zeroize all four private seeds. Idempotent. */
|
|
154
|
+
destroy() {
|
|
155
|
+
if (this.#destroyed)
|
|
156
|
+
return;
|
|
157
|
+
this.root.destroy();
|
|
158
|
+
this.public.destroy();
|
|
159
|
+
this.circle.destroy();
|
|
160
|
+
this.self.destroy();
|
|
161
|
+
this.#destroyed = true;
|
|
162
|
+
}
|
|
163
|
+
get destroyed() {
|
|
164
|
+
return this.#destroyed;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function asRawSeed(s, role) {
|
|
168
|
+
if (!(s instanceof RawSeedSigner)) {
|
|
169
|
+
throw new Error(`OwnerSigners._unsafeStoredIdentity: signer for "${role}" is not a RawSeedSigner; this method is incompatible with non-extractable key implementations.`);
|
|
170
|
+
}
|
|
171
|
+
return s;
|
|
172
|
+
}
|
|
173
|
+
function bytesToHexLocal(b) {
|
|
174
|
+
let out = "";
|
|
175
|
+
for (let i = 0; i < b.length; i++)
|
|
176
|
+
out += b[i].toString(16).padStart(2, "0");
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=owner-signers.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { KeyPair } from "@aithos/protocol-client";
|
|
2
|
+
import type { DelegateActor } from "./delegate-state.js";
|
|
3
|
+
import type { OwnerSigners } from "./owner-signers.js";
|
|
4
|
+
/** Extract the raw KeyPair for one of the owner's spheres. */
|
|
5
|
+
export declare function ownerKeyPair(owner: OwnerSigners, sphere: "root" | "public" | "circle" | "self"): KeyPair;
|
|
6
|
+
/** Extract the raw KeyPair held by a delegate actor. */
|
|
7
|
+
export declare function delegateKeyPair(actor: DelegateActor): KeyPair;
|
|
8
|
+
//# sourceMappingURL=protocol-client-bridge.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 Mathieu Colla
|
|
3
|
+
import { AithosSDKError } from "../types.js";
|
|
4
|
+
import { RawSeedSigner } from "./signer.js";
|
|
5
|
+
/** Extract the raw KeyPair for one of the owner's spheres. */
|
|
6
|
+
export function ownerKeyPair(owner, sphere) {
|
|
7
|
+
const signer = owner.signerForSphere(sphere);
|
|
8
|
+
return rawKeyPair(signer, `owner.${sphere}`);
|
|
9
|
+
}
|
|
10
|
+
/** Extract the raw KeyPair held by a delegate actor. */
|
|
11
|
+
export function delegateKeyPair(actor) {
|
|
12
|
+
return rawKeyPair(actor.signer, `delegate(${actor.mandateId})`);
|
|
13
|
+
}
|
|
14
|
+
function rawKeyPair(signer, role) {
|
|
15
|
+
if (!(signer instanceof RawSeedSigner)) {
|
|
16
|
+
throw new AithosSDKError("sdk_unsupported_signer", `${role}: signer is not RawSeedSigner — protocol-client interop requires raw seed access until upstream gains a Signer-shaped API`);
|
|
17
|
+
}
|
|
18
|
+
return signer._unsafeKeyPair();
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=protocol-client-bridge.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { BrowserIdentity } from "@aithos/protocol-client";
|
|
2
|
+
export interface ParsedRecoveryFile {
|
|
3
|
+
readonly handle: string;
|
|
4
|
+
readonly displayName: string;
|
|
5
|
+
readonly did: string;
|
|
6
|
+
readonly seedsHex: {
|
|
7
|
+
readonly root: string;
|
|
8
|
+
readonly public: string;
|
|
9
|
+
readonly circle: string;
|
|
10
|
+
readonly self: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Parse a recovery JSON string. Throws {@link AithosSDKError} with
|
|
15
|
+
* `code === "auth_invalid_recovery_file"` if the input is malformed.
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseRecoveryFile(text: string): ParsedRecoveryFile;
|
|
18
|
+
/** Read the file (Blob or already-decoded string) into UTF-8 text. */
|
|
19
|
+
export declare function readRecoveryFileText(file: Blob | string): Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* Build the recovery-file JSON blob from a fresh BrowserIdentity. Used
|
|
22
|
+
* by `signUp` so the same writer/reader code handles both ends of the
|
|
23
|
+
* round-trip.
|
|
24
|
+
*/
|
|
25
|
+
export declare function serializeRecoveryFile(identity: BrowserIdentity): {
|
|
26
|
+
readonly text: string;
|
|
27
|
+
readonly filename: string;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=recovery-file.d.ts.map
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 Mathieu Colla
|
|
3
|
+
import { AithosSDKError } from "../types.js";
|
|
4
|
+
const HEX_64 = /^[0-9a-f]{64}$/;
|
|
5
|
+
/**
|
|
6
|
+
* Parse a recovery JSON string. Throws {@link AithosSDKError} with
|
|
7
|
+
* `code === "auth_invalid_recovery_file"` if the input is malformed.
|
|
8
|
+
*/
|
|
9
|
+
export function parseRecoveryFile(text) {
|
|
10
|
+
let obj;
|
|
11
|
+
try {
|
|
12
|
+
obj = JSON.parse(text);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
throw bad("not valid JSON");
|
|
16
|
+
}
|
|
17
|
+
if (typeof obj !== "object" || obj === null) {
|
|
18
|
+
throw bad("not a JSON object");
|
|
19
|
+
}
|
|
20
|
+
const o = obj;
|
|
21
|
+
const ver = o["aithos_recovery_version"];
|
|
22
|
+
if (typeof ver !== "string" || !ver.startsWith("0.1.0")) {
|
|
23
|
+
throw bad(`unsupported aithos_recovery_version: ${String(ver)}`);
|
|
24
|
+
}
|
|
25
|
+
// Both shapes use snake_case for `display_name` and `seeds_hex`.
|
|
26
|
+
const handle = o["handle"];
|
|
27
|
+
const displayName = o["display_name"];
|
|
28
|
+
const did = o["did"];
|
|
29
|
+
const seedsRaw = o["seeds_hex"];
|
|
30
|
+
if (typeof handle !== "string" || !handle)
|
|
31
|
+
throw bad("missing handle");
|
|
32
|
+
if (typeof displayName !== "string")
|
|
33
|
+
throw bad("missing display_name");
|
|
34
|
+
if (typeof did !== "string" || !did.startsWith("did:")) {
|
|
35
|
+
throw bad("missing or malformed did");
|
|
36
|
+
}
|
|
37
|
+
if (typeof seedsRaw !== "object" || seedsRaw === null) {
|
|
38
|
+
throw bad("missing seeds_hex");
|
|
39
|
+
}
|
|
40
|
+
const seeds = seedsRaw;
|
|
41
|
+
for (const k of ["root", "public", "circle", "self"]) {
|
|
42
|
+
const v = seeds[k];
|
|
43
|
+
if (typeof v !== "string" || !HEX_64.test(v)) {
|
|
44
|
+
throw bad(`seeds_hex.${k}: expected 64-char lowercase hex`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
handle,
|
|
49
|
+
displayName,
|
|
50
|
+
did,
|
|
51
|
+
seedsHex: {
|
|
52
|
+
root: seeds["root"],
|
|
53
|
+
public: seeds["public"],
|
|
54
|
+
circle: seeds["circle"],
|
|
55
|
+
self: seeds["self"],
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Read the file (Blob or already-decoded string) into UTF-8 text. */
|
|
60
|
+
export async function readRecoveryFileText(file) {
|
|
61
|
+
if (typeof file === "string")
|
|
62
|
+
return file;
|
|
63
|
+
return file.text();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Build the recovery-file JSON blob from a fresh BrowserIdentity. Used
|
|
67
|
+
* by `signUp` so the same writer/reader code handles both ends of the
|
|
68
|
+
* round-trip.
|
|
69
|
+
*/
|
|
70
|
+
export function serializeRecoveryFile(identity) {
|
|
71
|
+
const payload = {
|
|
72
|
+
aithos_recovery_version: "0.1.0-plaintext",
|
|
73
|
+
handle: identity.handle,
|
|
74
|
+
display_name: identity.displayName,
|
|
75
|
+
did: identity.did,
|
|
76
|
+
seeds_hex: {
|
|
77
|
+
root: bytesToHex(identity.root.seed),
|
|
78
|
+
public: bytesToHex(identity.public.seed),
|
|
79
|
+
circle: bytesToHex(identity.circle.seed),
|
|
80
|
+
self: bytesToHex(identity.self.seed),
|
|
81
|
+
},
|
|
82
|
+
saved_at: new Date().toISOString(),
|
|
83
|
+
};
|
|
84
|
+
return {
|
|
85
|
+
text: JSON.stringify(payload, null, 2),
|
|
86
|
+
filename: `aithos-recovery-${identity.handle}.json`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function bad(detail) {
|
|
90
|
+
return new AithosSDKError("auth_invalid_recovery_file", `recovery file is invalid: ${detail}`);
|
|
91
|
+
}
|
|
92
|
+
function bytesToHex(b) {
|
|
93
|
+
let out = "";
|
|
94
|
+
for (let i = 0; i < b.length; i++)
|
|
95
|
+
out += b[i].toString(16).padStart(2, "0");
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=recovery-file.js.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type KeyPair } from "@aithos/protocol-client";
|
|
2
|
+
/**
|
|
3
|
+
* Capability to produce Ed25519 signatures over a fixed key. The key
|
|
4
|
+
* material itself is not exposed — only the public key (which is, by
|
|
5
|
+
* definition, public) and the {@link sign} method.
|
|
6
|
+
*
|
|
7
|
+
* `sign` returns a Promise<Uint8Array> even when the underlying
|
|
8
|
+
* implementation is synchronous (e.g. {@link RawSeedSigner}) so future
|
|
9
|
+
* implementations backed by `crypto.subtle.sign` can drop in without
|
|
10
|
+
* breaking callers.
|
|
11
|
+
*/
|
|
12
|
+
export interface Signer {
|
|
13
|
+
/** 32-byte Ed25519 public key. */
|
|
14
|
+
readonly publicKey: Uint8Array;
|
|
15
|
+
/** Sign `message` and return the 64-byte Ed25519 signature. */
|
|
16
|
+
sign(message: Uint8Array): Promise<Uint8Array>;
|
|
17
|
+
/** Zeroize any private material the signer holds. Idempotent. */
|
|
18
|
+
destroy(): void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Today's implementation: wraps a raw 32-byte Ed25519 seed and signs
|
|
22
|
+
* via `@noble/ed25519`. Holds the seed in a defensively-copied
|
|
23
|
+
* Uint8Array bound to a private field, so mutating the constructor
|
|
24
|
+
* input afterwards does not affect the signer.
|
|
25
|
+
*
|
|
26
|
+
* Migration note: when we move to Web Crypto non-extractable keys,
|
|
27
|
+
* this class is replaced by a `SubtleSigner` that holds a `CryptoKey`
|
|
28
|
+
* reference and calls `crypto.subtle.sign("Ed25519", key, message)`.
|
|
29
|
+
* The {@link Signer} interface stays the same, so all callers (the
|
|
30
|
+
* SessionVault, the auth namespace, the ethos publish path) keep
|
|
31
|
+
* working unchanged.
|
|
32
|
+
*/
|
|
33
|
+
export declare class RawSeedSigner implements Signer {
|
|
34
|
+
#private;
|
|
35
|
+
readonly publicKey: Uint8Array;
|
|
36
|
+
/**
|
|
37
|
+
* @param seed 32-byte Ed25519 seed (the private half).
|
|
38
|
+
* @param publicKey 32-byte Ed25519 public key matching `seed`.
|
|
39
|
+
* Both arrays are defensively copied — the caller may zeroize their
|
|
40
|
+
* originals immediately.
|
|
41
|
+
*/
|
|
42
|
+
constructor(seed: Uint8Array, publicKey: Uint8Array);
|
|
43
|
+
sign(message: Uint8Array): Promise<Uint8Array>;
|
|
44
|
+
destroy(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Internal escape hatch for protocol-client interop. Returns a KeyPair
|
|
47
|
+
* usable with `buildSignedEnvelope({ signer })` and friends, which
|
|
48
|
+
* today take a raw seed. Marked `_unsafe` because it surfaces the
|
|
49
|
+
* private seed bytes — only callers within `@aithos/sdk` should use
|
|
50
|
+
* it, and never propagate the result outside the SDK boundary.
|
|
51
|
+
*
|
|
52
|
+
* When protocol-client gains a Signer-shaped API (post-alpha), this
|
|
53
|
+
* method goes away and SubtleSigner becomes pluggable upstream.
|
|
54
|
+
*
|
|
55
|
+
* @internal
|
|
56
|
+
*/
|
|
57
|
+
_unsafeKeyPair(): KeyPair;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=signer.d.ts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 Mathieu Colla
|
|
3
|
+
// Internal Signer abstraction.
|
|
4
|
+
//
|
|
5
|
+
// SDK-internal only — NOT exported from the package barrel. The whole
|
|
6
|
+
// point of this file is to draw the line between "what the SDK consumer
|
|
7
|
+
// sees" (no seeds, no key bytes, just verbs like `addSection` and
|
|
8
|
+
// `publish`) and "how the SDK signs internally" (today: raw Ed25519
|
|
9
|
+
// seeds via @noble; tomorrow: non-extractable CryptoKeys via
|
|
10
|
+
// crypto.subtle).
|
|
11
|
+
//
|
|
12
|
+
// The {@link Signer} interface is the contract. {@link RawSeedSigner}
|
|
13
|
+
// is today's implementation. When we migrate to Web Crypto
|
|
14
|
+
// non-extractable keys, a {@link SubtleSigner} drops in beside it
|
|
15
|
+
// implementing the same interface, and nothing visible to the SDK
|
|
16
|
+
// consumer changes.
|
|
17
|
+
//
|
|
18
|
+
// `sign` is async-shaped from the start so the migration to
|
|
19
|
+
// `crypto.subtle.sign` (which is async) doesn't ripple through the
|
|
20
|
+
// caller graph as a breaking interface change.
|
|
21
|
+
import { sign as ed25519Sign } from "@aithos/protocol-client";
|
|
22
|
+
/**
|
|
23
|
+
* Today's implementation: wraps a raw 32-byte Ed25519 seed and signs
|
|
24
|
+
* via `@noble/ed25519`. Holds the seed in a defensively-copied
|
|
25
|
+
* Uint8Array bound to a private field, so mutating the constructor
|
|
26
|
+
* input afterwards does not affect the signer.
|
|
27
|
+
*
|
|
28
|
+
* Migration note: when we move to Web Crypto non-extractable keys,
|
|
29
|
+
* this class is replaced by a `SubtleSigner` that holds a `CryptoKey`
|
|
30
|
+
* reference and calls `crypto.subtle.sign("Ed25519", key, message)`.
|
|
31
|
+
* The {@link Signer} interface stays the same, so all callers (the
|
|
32
|
+
* SessionVault, the auth namespace, the ethos publish path) keep
|
|
33
|
+
* working unchanged.
|
|
34
|
+
*/
|
|
35
|
+
export class RawSeedSigner {
|
|
36
|
+
publicKey;
|
|
37
|
+
#seed;
|
|
38
|
+
#destroyed = false;
|
|
39
|
+
/**
|
|
40
|
+
* @param seed 32-byte Ed25519 seed (the private half).
|
|
41
|
+
* @param publicKey 32-byte Ed25519 public key matching `seed`.
|
|
42
|
+
* Both arrays are defensively copied — the caller may zeroize their
|
|
43
|
+
* originals immediately.
|
|
44
|
+
*/
|
|
45
|
+
constructor(seed, publicKey) {
|
|
46
|
+
if (seed.length !== 32) {
|
|
47
|
+
throw new Error(`RawSeedSigner: seed must be 32 bytes, got ${seed.length}`);
|
|
48
|
+
}
|
|
49
|
+
if (publicKey.length !== 32) {
|
|
50
|
+
throw new Error(`RawSeedSigner: publicKey must be 32 bytes, got ${publicKey.length}`);
|
|
51
|
+
}
|
|
52
|
+
this.#seed = new Uint8Array(seed);
|
|
53
|
+
this.publicKey = new Uint8Array(publicKey);
|
|
54
|
+
}
|
|
55
|
+
async sign(message) {
|
|
56
|
+
if (this.#destroyed) {
|
|
57
|
+
throw new Error("RawSeedSigner: cannot sign with a destroyed signer");
|
|
58
|
+
}
|
|
59
|
+
return ed25519Sign(message, this.#seed);
|
|
60
|
+
}
|
|
61
|
+
destroy() {
|
|
62
|
+
if (this.#destroyed)
|
|
63
|
+
return;
|
|
64
|
+
this.#seed.fill(0);
|
|
65
|
+
this.#destroyed = true;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Internal escape hatch for protocol-client interop. Returns a KeyPair
|
|
69
|
+
* usable with `buildSignedEnvelope({ signer })` and friends, which
|
|
70
|
+
* today take a raw seed. Marked `_unsafe` because it surfaces the
|
|
71
|
+
* private seed bytes — only callers within `@aithos/sdk` should use
|
|
72
|
+
* it, and never propagate the result outside the SDK boundary.
|
|
73
|
+
*
|
|
74
|
+
* When protocol-client gains a Signer-shaped API (post-alpha), this
|
|
75
|
+
* method goes away and SubtleSigner becomes pluggable upstream.
|
|
76
|
+
*
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
_unsafeKeyPair() {
|
|
80
|
+
if (this.#destroyed) {
|
|
81
|
+
throw new Error("RawSeedSigner: cannot use a destroyed signer");
|
|
82
|
+
}
|
|
83
|
+
return { seed: this.#seed, publicKey: this.publicKey };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=signer.js.map
|