@aster-rpc/aster 0.1.2
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/dist/capabilities.d.ts +26 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +29 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/client.d.ts +65 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +108 -0
- package/dist/client.js.map +1 -0
- package/dist/codec.d.ts +156 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +477 -0
- package/dist/codec.js.map +1 -0
- package/dist/config.d.ts +102 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +454 -0
- package/dist/config.js.map +1 -0
- package/dist/contract/identity.d.ts +115 -0
- package/dist/contract/identity.d.ts.map +1 -0
- package/dist/contract/identity.js +188 -0
- package/dist/contract/identity.js.map +1 -0
- package/dist/contract/manifest.d.ts +77 -0
- package/dist/contract/manifest.d.ts.map +1 -0
- package/dist/contract/manifest.js +127 -0
- package/dist/contract/manifest.js.map +1 -0
- package/dist/contract/publication.d.ts +71 -0
- package/dist/contract/publication.d.ts.map +1 -0
- package/dist/contract/publication.js +85 -0
- package/dist/contract/publication.js.map +1 -0
- package/dist/decorators.d.ts +139 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +175 -0
- package/dist/decorators.js.map +1 -0
- package/dist/dynamic.d.ts +61 -0
- package/dist/dynamic.d.ts.map +1 -0
- package/dist/dynamic.js +147 -0
- package/dist/dynamic.js.map +1 -0
- package/dist/framing.d.ts +74 -0
- package/dist/framing.d.ts.map +1 -0
- package/dist/framing.js +162 -0
- package/dist/framing.js.map +1 -0
- package/dist/health.d.ts +127 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +236 -0
- package/dist/health.js.map +1 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +101 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptors/audit.d.ts +25 -0
- package/dist/interceptors/audit.d.ts.map +1 -0
- package/dist/interceptors/audit.js +46 -0
- package/dist/interceptors/audit.js.map +1 -0
- package/dist/interceptors/auth.d.ts +13 -0
- package/dist/interceptors/auth.d.ts.map +1 -0
- package/dist/interceptors/auth.js +34 -0
- package/dist/interceptors/auth.js.map +1 -0
- package/dist/interceptors/base.d.ts +74 -0
- package/dist/interceptors/base.d.ts.map +1 -0
- package/dist/interceptors/base.js +103 -0
- package/dist/interceptors/base.js.map +1 -0
- package/dist/interceptors/capability.d.ts +16 -0
- package/dist/interceptors/capability.d.ts.map +1 -0
- package/dist/interceptors/capability.js +63 -0
- package/dist/interceptors/capability.js.map +1 -0
- package/dist/interceptors/circuit-breaker.d.ts +40 -0
- package/dist/interceptors/circuit-breaker.d.ts.map +1 -0
- package/dist/interceptors/circuit-breaker.js +91 -0
- package/dist/interceptors/circuit-breaker.js.map +1 -0
- package/dist/interceptors/compression.d.ts +11 -0
- package/dist/interceptors/compression.d.ts.map +1 -0
- package/dist/interceptors/compression.js +12 -0
- package/dist/interceptors/compression.js.map +1 -0
- package/dist/interceptors/deadline.d.ts +12 -0
- package/dist/interceptors/deadline.d.ts.map +1 -0
- package/dist/interceptors/deadline.js +28 -0
- package/dist/interceptors/deadline.js.map +1 -0
- package/dist/interceptors/metrics.d.ts +43 -0
- package/dist/interceptors/metrics.d.ts.map +1 -0
- package/dist/interceptors/metrics.js +132 -0
- package/dist/interceptors/metrics.js.map +1 -0
- package/dist/interceptors/rate-limit.d.ts +24 -0
- package/dist/interceptors/rate-limit.d.ts.map +1 -0
- package/dist/interceptors/rate-limit.js +84 -0
- package/dist/interceptors/rate-limit.js.map +1 -0
- package/dist/interceptors/retry.d.ts +25 -0
- package/dist/interceptors/retry.d.ts.map +1 -0
- package/dist/interceptors/retry.js +55 -0
- package/dist/interceptors/retry.js.map +1 -0
- package/dist/limits.d.ts +77 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +137 -0
- package/dist/limits.js.map +1 -0
- package/dist/logging.d.ts +40 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +92 -0
- package/dist/logging.js.map +1 -0
- package/dist/metadata.d.ts +14 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +68 -0
- package/dist/metadata.js.map +1 -0
- package/dist/metrics.d.ts +40 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +92 -0
- package/dist/metrics.js.map +1 -0
- package/dist/peer-store.d.ts +53 -0
- package/dist/peer-store.d.ts.map +1 -0
- package/dist/peer-store.js +105 -0
- package/dist/peer-store.js.map +1 -0
- package/dist/protocol.d.ts +44 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +59 -0
- package/dist/protocol.js.map +1 -0
- package/dist/registration.d.ts +81 -0
- package/dist/registration.d.ts.map +1 -0
- package/dist/registration.js +161 -0
- package/dist/registration.js.map +1 -0
- package/dist/registry/acl.d.ts +57 -0
- package/dist/registry/acl.d.ts.map +1 -0
- package/dist/registry/acl.js +104 -0
- package/dist/registry/acl.js.map +1 -0
- package/dist/registry/client.d.ts +70 -0
- package/dist/registry/client.d.ts.map +1 -0
- package/dist/registry/client.js +115 -0
- package/dist/registry/client.js.map +1 -0
- package/dist/registry/gossip.d.ts +43 -0
- package/dist/registry/gossip.d.ts.map +1 -0
- package/dist/registry/gossip.js +102 -0
- package/dist/registry/gossip.js.map +1 -0
- package/dist/registry/keys.d.ts +25 -0
- package/dist/registry/keys.d.ts.map +1 -0
- package/dist/registry/keys.js +47 -0
- package/dist/registry/keys.js.map +1 -0
- package/dist/registry/models.d.ts +80 -0
- package/dist/registry/models.d.ts.map +1 -0
- package/dist/registry/models.js +35 -0
- package/dist/registry/models.js.map +1 -0
- package/dist/registry/publisher.d.ts +65 -0
- package/dist/registry/publisher.d.ts.map +1 -0
- package/dist/registry/publisher.js +164 -0
- package/dist/registry/publisher.js.map +1 -0
- package/dist/runtime.d.ts +267 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1366 -0
- package/dist/runtime.js.map +1 -0
- package/dist/server.d.ts +100 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +511 -0
- package/dist/server.js.map +1 -0
- package/dist/service.d.ts +72 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +98 -0
- package/dist/service.js.map +1 -0
- package/dist/session.d.ts +64 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +350 -0
- package/dist/session.js.map +1 -0
- package/dist/status.d.ts +113 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +206 -0
- package/dist/status.js.map +1 -0
- package/dist/transport/base.d.ts +46 -0
- package/dist/transport/base.d.ts.map +1 -0
- package/dist/transport/base.js +10 -0
- package/dist/transport/base.js.map +1 -0
- package/dist/transport/iroh.d.ts +45 -0
- package/dist/transport/iroh.d.ts.map +1 -0
- package/dist/transport/iroh.js +225 -0
- package/dist/transport/iroh.js.map +1 -0
- package/dist/transport/local.d.ts +48 -0
- package/dist/transport/local.d.ts.map +1 -0
- package/dist/transport/local.js +139 -0
- package/dist/transport/local.js.map +1 -0
- package/dist/trust/admission.d.ts +60 -0
- package/dist/trust/admission.d.ts.map +1 -0
- package/dist/trust/admission.js +149 -0
- package/dist/trust/admission.js.map +1 -0
- package/dist/trust/bootstrap.d.ts +109 -0
- package/dist/trust/bootstrap.d.ts.map +1 -0
- package/dist/trust/bootstrap.js +311 -0
- package/dist/trust/bootstrap.js.map +1 -0
- package/dist/trust/clock.d.ts +93 -0
- package/dist/trust/clock.d.ts.map +1 -0
- package/dist/trust/clock.js +154 -0
- package/dist/trust/clock.js.map +1 -0
- package/dist/trust/consumer.d.ts +139 -0
- package/dist/trust/consumer.d.ts.map +1 -0
- package/dist/trust/consumer.js +323 -0
- package/dist/trust/consumer.js.map +1 -0
- package/dist/trust/credentials.d.ts +98 -0
- package/dist/trust/credentials.d.ts.map +1 -0
- package/dist/trust/credentials.js +250 -0
- package/dist/trust/credentials.js.map +1 -0
- package/dist/trust/delegated.d.ts +118 -0
- package/dist/trust/delegated.d.ts.map +1 -0
- package/dist/trust/delegated.js +292 -0
- package/dist/trust/delegated.js.map +1 -0
- package/dist/trust/gossip.d.ts +146 -0
- package/dist/trust/gossip.d.ts.map +1 -0
- package/dist/trust/gossip.js +334 -0
- package/dist/trust/gossip.js.map +1 -0
- package/dist/trust/hooks.d.ts +84 -0
- package/dist/trust/hooks.d.ts.map +1 -0
- package/dist/trust/hooks.js +125 -0
- package/dist/trust/hooks.js.map +1 -0
- package/dist/trust/iid.d.ts +65 -0
- package/dist/trust/iid.d.ts.map +1 -0
- package/dist/trust/iid.js +104 -0
- package/dist/trust/iid.js.map +1 -0
- package/dist/trust/mesh.d.ts +43 -0
- package/dist/trust/mesh.d.ts.map +1 -0
- package/dist/trust/mesh.js +105 -0
- package/dist/trust/mesh.js.map +1 -0
- package/dist/trust/nonce.d.ts +39 -0
- package/dist/trust/nonce.d.ts.map +1 -0
- package/dist/trust/nonce.js +46 -0
- package/dist/trust/nonce.js.map +1 -0
- package/dist/trust/producer.d.ts +80 -0
- package/dist/trust/producer.d.ts.map +1 -0
- package/dist/trust/producer.js +151 -0
- package/dist/trust/producer.js.map +1 -0
- package/dist/trust/rcan.d.ts +29 -0
- package/dist/trust/rcan.d.ts.map +1 -0
- package/dist/trust/rcan.js +57 -0
- package/dist/trust/rcan.js.map +1 -0
- package/dist/types.d.ts +57 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +50 -0
- package/dist/types.js.map +1 -0
- package/dist/xlang.d.ts +26 -0
- package/dist/xlang.d.ts.map +1 -0
- package/dist/xlang.js +55 -0
- package/dist/xlang.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential types and ed25519 signing/verification.
|
|
3
|
+
*
|
|
4
|
+
* Spec reference: Aster-trust-spec.md
|
|
5
|
+
*
|
|
6
|
+
* Signing bytes computation delegates to Rust core via NAPI.
|
|
7
|
+
* Ed25519 operations use @noble/ed25519 for portability.
|
|
8
|
+
*/
|
|
9
|
+
/** Reserved attribute keys. */
|
|
10
|
+
export const ATTR_ROLE = 'aster.role';
|
|
11
|
+
export const ATTR_NAME = 'aster.name';
|
|
12
|
+
export const ATTR_IID_PROVIDER = 'aster.iid_provider';
|
|
13
|
+
export const ATTR_IID_ACCOUNT = 'aster.iid_account';
|
|
14
|
+
export const ATTR_IID_REGION = 'aster.iid_region';
|
|
15
|
+
export const ATTR_IID_ROLE_ARN = 'aster.iid_role_arn';
|
|
16
|
+
let _edModule; // undefined = not tried, null = unavailable
|
|
17
|
+
let _nodeCrypto;
|
|
18
|
+
async function getEd() {
|
|
19
|
+
if (_edModule !== undefined)
|
|
20
|
+
return _edModule;
|
|
21
|
+
try {
|
|
22
|
+
// @ts-ignore — optional dependency
|
|
23
|
+
_edModule = (await import('@noble/ed25519'));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
_edModule = null;
|
|
27
|
+
}
|
|
28
|
+
return _edModule;
|
|
29
|
+
}
|
|
30
|
+
async function getNodeCrypto() {
|
|
31
|
+
if (_nodeCrypto)
|
|
32
|
+
return _nodeCrypto;
|
|
33
|
+
_nodeCrypto = await import('node:crypto');
|
|
34
|
+
return _nodeCrypto;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Generate an ed25519 keypair.
|
|
38
|
+
* Returns [privateKey (32 bytes), publicKey (32 bytes)].
|
|
39
|
+
*
|
|
40
|
+
* Requires @noble/ed25519:
|
|
41
|
+
* ```ts
|
|
42
|
+
* import { utils } from '@noble/ed25519';
|
|
43
|
+
* const privKey = utils.randomPrivateKey();
|
|
44
|
+
* const pubKey = await getPublicKeyAsync(privKey);
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export async function generateKeypair() {
|
|
48
|
+
const ed = await getEd();
|
|
49
|
+
if (ed) {
|
|
50
|
+
const privateKey = ed.utils.randomPrivateKey();
|
|
51
|
+
const publicKey = await ed.getPublicKeyAsync(privateKey);
|
|
52
|
+
return [privateKey, publicKey];
|
|
53
|
+
}
|
|
54
|
+
const { generateKeyPairSync } = await getNodeCrypto();
|
|
55
|
+
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
|
|
56
|
+
const privBuf = privateKey.export({ type: 'pkcs8', format: 'der' }).subarray(-32);
|
|
57
|
+
const pubBuf = publicKey.export({ type: 'spki', format: 'der' }).subarray(-32);
|
|
58
|
+
return [new Uint8Array(privBuf), new Uint8Array(pubBuf)];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Sign a message with an ed25519 private key.
|
|
62
|
+
* Returns 64-byte signature.
|
|
63
|
+
*/
|
|
64
|
+
export async function sign(privateKey, message) {
|
|
65
|
+
const ed = await getEd();
|
|
66
|
+
if (ed) {
|
|
67
|
+
return ed.signAsync(message, privateKey);
|
|
68
|
+
}
|
|
69
|
+
const { sign: cryptoSign, createPrivateKey } = await getNodeCrypto();
|
|
70
|
+
const key = createPrivateKey({
|
|
71
|
+
key: Buffer.concat([
|
|
72
|
+
Buffer.from('302e020100300506032b657004220420', 'hex'),
|
|
73
|
+
Buffer.from(privateKey),
|
|
74
|
+
]),
|
|
75
|
+
format: 'der',
|
|
76
|
+
type: 'pkcs8',
|
|
77
|
+
});
|
|
78
|
+
return new Uint8Array(cryptoSign(null, Buffer.from(message), key));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Verify an ed25519 signature.
|
|
82
|
+
*/
|
|
83
|
+
export async function verify(publicKey, message, signature) {
|
|
84
|
+
const ed = await getEd();
|
|
85
|
+
if (ed) {
|
|
86
|
+
return ed.verifyAsync(signature, message, publicKey);
|
|
87
|
+
}
|
|
88
|
+
const { verify: cryptoVerify, createPublicKey } = await getNodeCrypto();
|
|
89
|
+
const key = createPublicKey({
|
|
90
|
+
key: Buffer.concat([
|
|
91
|
+
Buffer.from('302a300506032b6570032100', 'hex'),
|
|
92
|
+
Buffer.from(publicKey),
|
|
93
|
+
]),
|
|
94
|
+
format: 'der',
|
|
95
|
+
type: 'spki',
|
|
96
|
+
});
|
|
97
|
+
return cryptoVerify(null, Buffer.from(message), key, Buffer.from(signature));
|
|
98
|
+
}
|
|
99
|
+
// ── Hex helpers ──────────────────────────────────────────────────────────────
|
|
100
|
+
function hexToBytes(hex) {
|
|
101
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
102
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
103
|
+
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
104
|
+
}
|
|
105
|
+
return bytes;
|
|
106
|
+
}
|
|
107
|
+
function bytesToHex(bytes) {
|
|
108
|
+
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
109
|
+
}
|
|
110
|
+
// ── Canonical signing bytes ─────────────────────────────────────────────────
|
|
111
|
+
// Spec reference: Aster-trust-spec.md §2.2, §2.4
|
|
112
|
+
// Authoritative implementation: core/src/signing.rs
|
|
113
|
+
// This TS version mirrors the Python helper for credential sign/verify.
|
|
114
|
+
/** Canonical JSON: UTF-8, sorted keys, no extra whitespace. */
|
|
115
|
+
export function canonicalJson(attributes) {
|
|
116
|
+
const sorted = Object.keys(attributes).sort();
|
|
117
|
+
const obj = {};
|
|
118
|
+
for (const k of sorted)
|
|
119
|
+
obj[k] = attributes[k];
|
|
120
|
+
const json = JSON.stringify(obj);
|
|
121
|
+
return new TextEncoder().encode(json);
|
|
122
|
+
}
|
|
123
|
+
/** u64 big-endian encoding. */
|
|
124
|
+
function writeU64BE(value) {
|
|
125
|
+
const buf = new ArrayBuffer(8);
|
|
126
|
+
const view = new DataView(buf);
|
|
127
|
+
// JS numbers are safe up to 2^53; for epoch seconds this is fine
|
|
128
|
+
view.setUint32(0, Math.floor(value / 0x100000000));
|
|
129
|
+
view.setUint32(4, value >>> 0);
|
|
130
|
+
return new Uint8Array(buf);
|
|
131
|
+
}
|
|
132
|
+
/** Signing bytes for EnrollmentCredential (producer). */
|
|
133
|
+
export function producerSigningBytes(cred) {
|
|
134
|
+
const parts = [
|
|
135
|
+
new TextEncoder().encode(cred.endpointId),
|
|
136
|
+
hexToBytes(cred.rootPubkey), // 32 bytes
|
|
137
|
+
writeU64BE(cred.expiresAt), // 8 bytes
|
|
138
|
+
canonicalJson(cred.attributes),
|
|
139
|
+
];
|
|
140
|
+
return concatBytes(parts);
|
|
141
|
+
}
|
|
142
|
+
/** Signing bytes for ConsumerEnrollmentCredential. */
|
|
143
|
+
export function consumerSigningBytes(cred) {
|
|
144
|
+
const typeCode = cred.credentialType === 'ott' ? new Uint8Array([0x01]) : new Uint8Array([0x00]);
|
|
145
|
+
let eidPart;
|
|
146
|
+
if (cred.endpointId != null) {
|
|
147
|
+
const eidBytes = new TextEncoder().encode(cred.endpointId);
|
|
148
|
+
eidPart = concatBytes([new Uint8Array([0x01]), eidBytes]);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
eidPart = new Uint8Array([0x00]);
|
|
152
|
+
}
|
|
153
|
+
let noncePart;
|
|
154
|
+
if (cred.nonce != null) {
|
|
155
|
+
noncePart = concatBytes([new Uint8Array([0x01]), hexToBytes(cred.nonce)]);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
noncePart = new Uint8Array([0x00]);
|
|
159
|
+
}
|
|
160
|
+
return concatBytes([
|
|
161
|
+
typeCode,
|
|
162
|
+
eidPart,
|
|
163
|
+
hexToBytes(cred.rootPubkey), // 32 bytes
|
|
164
|
+
writeU64BE(cred.expiresAt), // 8 bytes
|
|
165
|
+
canonicalJson(cred.attributes),
|
|
166
|
+
noncePart,
|
|
167
|
+
]);
|
|
168
|
+
}
|
|
169
|
+
/** Compute signing bytes for any credential type. */
|
|
170
|
+
export function credentialSigningBytes(cred) {
|
|
171
|
+
if ('endpointId' in cred && 'credentialType' in cred) {
|
|
172
|
+
return consumerSigningBytes(cred);
|
|
173
|
+
}
|
|
174
|
+
if ('endpointId' in cred && !('credentialType' in cred)) {
|
|
175
|
+
return producerSigningBytes(cred);
|
|
176
|
+
}
|
|
177
|
+
throw new Error(`Unsupported credential type`);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Sign a credential with the root private key.
|
|
181
|
+
* Returns 64-byte signature as hex string.
|
|
182
|
+
*/
|
|
183
|
+
export async function signCredential(cred, rootPrivkeyRaw) {
|
|
184
|
+
const msg = credentialSigningBytes(cred);
|
|
185
|
+
const sig = await sign(rootPrivkeyRaw, msg);
|
|
186
|
+
return bytesToHex(sig);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Verify a credential's signature.
|
|
190
|
+
* If rootPubkeyHex is provided, it overrides cred.rootPubkey.
|
|
191
|
+
* Returns true on success, false on any failure.
|
|
192
|
+
*/
|
|
193
|
+
export async function verifyCredentialSignature(cred, rootPubkeyHex) {
|
|
194
|
+
const pubkeyHex = rootPubkeyHex ?? cred.rootPubkey;
|
|
195
|
+
try {
|
|
196
|
+
const msg = credentialSigningBytes(cred);
|
|
197
|
+
return await verify(hexToBytes(pubkeyHex), msg, hexToBytes(cred.signature));
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
204
|
+
function concatBytes(arrays) {
|
|
205
|
+
const totalLen = arrays.reduce((sum, a) => sum + a.length, 0);
|
|
206
|
+
const result = new Uint8Array(totalLen);
|
|
207
|
+
let offset = 0;
|
|
208
|
+
for (const a of arrays) {
|
|
209
|
+
result.set(a, offset);
|
|
210
|
+
offset += a.length;
|
|
211
|
+
}
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
// ── Key utilities ─────────────────────────────────────────────────────────────
|
|
215
|
+
/**
|
|
216
|
+
* Generate a root keypair (alias for generateKeypair).
|
|
217
|
+
* Returns [privateKey, publicKey] as raw 32-byte arrays.
|
|
218
|
+
*/
|
|
219
|
+
export async function generateRootKeypair() {
|
|
220
|
+
return generateKeypair();
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Load a raw 32-byte private key.
|
|
224
|
+
* Returns the key as-is (validates length).
|
|
225
|
+
*/
|
|
226
|
+
export function loadPrivateKey(privRaw) {
|
|
227
|
+
if (privRaw.byteLength !== 32) {
|
|
228
|
+
throw new TypeError(`Invalid private key length: expected 32, got ${privRaw.byteLength}`);
|
|
229
|
+
}
|
|
230
|
+
return privRaw;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Load a raw 32-byte public key.
|
|
234
|
+
* Returns the key as-is (validates length).
|
|
235
|
+
*/
|
|
236
|
+
export function loadPublicKey(pubRaw) {
|
|
237
|
+
if (pubRaw.byteLength !== 32) {
|
|
238
|
+
throw new TypeError(`Invalid public key length: expected 32, got ${pubRaw.byteLength}`);
|
|
239
|
+
}
|
|
240
|
+
return pubRaw;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Verify an ed25519 signature.
|
|
244
|
+
* Alias for the low-level `verify()` with a consistent signature.
|
|
245
|
+
*/
|
|
246
|
+
export async function verifySignature(publicKey, message, signature) {
|
|
247
|
+
return verify(publicKey, message, signature);
|
|
248
|
+
}
|
|
249
|
+
export { hexToBytes, bytesToHex, concatBytes };
|
|
250
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/trust/credentials.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* aster/trust/delegated -- Delegated admission via @aster-issued enrollment tokens.
|
|
3
|
+
*
|
|
4
|
+
* Implements the `aster.admission` ALPN handler (Aster-trust-spec S3a).
|
|
5
|
+
*
|
|
6
|
+
* Protocol:
|
|
7
|
+
* 1. Consumer sends: EnrollmentToken (JSON) + SigningKeyAttestation (JSON)
|
|
8
|
+
* 2. Service verifies: attestation -> token -> service binding
|
|
9
|
+
* 3. Service sends: AdmissionChallenge (32-byte nonce + service identity)
|
|
10
|
+
* 4. Consumer sends: AdmissionProof (signature over challenge with root key)
|
|
11
|
+
* 5. Service verifies: proof of possession
|
|
12
|
+
* 6. Service admits with: {handle, roles}
|
|
13
|
+
*
|
|
14
|
+
* All crypto is ed25519. No network calls -- verification uses cached
|
|
15
|
+
* attestations and the @aster root pubkey received at publish time.
|
|
16
|
+
*/
|
|
17
|
+
import type { MeshEndpointHook } from './hooks.js';
|
|
18
|
+
import type { PeerAttributeStore } from '../peer-store.js';
|
|
19
|
+
/** Attestation binding a signing key to the @aster root key. */
|
|
20
|
+
export interface SigningKeyAttestation {
|
|
21
|
+
signingPubkey: string;
|
|
22
|
+
keyId: string;
|
|
23
|
+
validFrom: number;
|
|
24
|
+
validUntil: number;
|
|
25
|
+
rootSignature: string;
|
|
26
|
+
}
|
|
27
|
+
/** @aster-issued token granting a consumer access to a service. */
|
|
28
|
+
export interface EnrollmentToken {
|
|
29
|
+
consumerHandle: string;
|
|
30
|
+
consumerPubkey: string;
|
|
31
|
+
targetHandle: string;
|
|
32
|
+
targetService: string;
|
|
33
|
+
targetContractId: string;
|
|
34
|
+
roles: string[];
|
|
35
|
+
issuedAt: number;
|
|
36
|
+
expiresAt: number;
|
|
37
|
+
signingKeyId: string;
|
|
38
|
+
signature: string;
|
|
39
|
+
}
|
|
40
|
+
/** Service-side policy for verifying delegated tokens. */
|
|
41
|
+
export interface DelegatedAdmissionPolicy {
|
|
42
|
+
targetHandle: string;
|
|
43
|
+
targetService: string;
|
|
44
|
+
targetContractId: string;
|
|
45
|
+
/** Hex-encoded @aster root public key -- the trust anchor. */
|
|
46
|
+
asterRootPubkey: string;
|
|
47
|
+
}
|
|
48
|
+
/** Result of a successful delegated admission. */
|
|
49
|
+
export interface DelegatedAdmissionResult {
|
|
50
|
+
admitted: boolean;
|
|
51
|
+
handle: string;
|
|
52
|
+
roles: string[];
|
|
53
|
+
consumerPubkey: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Verify a signing-key attestation against the @aster root pubkey.
|
|
57
|
+
* Throws RpcError on failure.
|
|
58
|
+
*/
|
|
59
|
+
export declare function verifyAttestation(attestation: SigningKeyAttestation, opts: {
|
|
60
|
+
asterRootPubkeyHex: string;
|
|
61
|
+
now?: number;
|
|
62
|
+
}): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Verify an enrollment token against the attestation and service policy.
|
|
65
|
+
* Throws RpcError on failure.
|
|
66
|
+
*/
|
|
67
|
+
export declare function verifyToken(token: EnrollmentToken, attestation: SigningKeyAttestation, opts: {
|
|
68
|
+
policy: DelegatedAdmissionPolicy;
|
|
69
|
+
now?: number;
|
|
70
|
+
}): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Verify the consumer's proof of possession of their root key.
|
|
73
|
+
* Throws RpcError on failure.
|
|
74
|
+
*/
|
|
75
|
+
export declare function verifyProofOfPossession(opts: {
|
|
76
|
+
consumerPubkeyHex: string;
|
|
77
|
+
challengeBytes: Uint8Array;
|
|
78
|
+
signatureHex: string;
|
|
79
|
+
}): Promise<void>;
|
|
80
|
+
/** Build the challenge payload that the consumer must sign. */
|
|
81
|
+
export declare function buildChallengeBytes(nonce: Uint8Array, targetHandle: string, targetService: string, alpn?: string): Uint8Array;
|
|
82
|
+
/**
|
|
83
|
+
* Bidirectional stream abstraction expected by the handler.
|
|
84
|
+
* Matches the shape provided by Iroh QUIC connections.
|
|
85
|
+
*/
|
|
86
|
+
export interface BiStream {
|
|
87
|
+
send: {
|
|
88
|
+
writeAll(data: Uint8Array): Promise<void>;
|
|
89
|
+
finish(): Promise<void>;
|
|
90
|
+
};
|
|
91
|
+
recv: {
|
|
92
|
+
readToEnd(maxBytes: number): Promise<Uint8Array | null>;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Connection abstraction expected by the handler.
|
|
97
|
+
*/
|
|
98
|
+
export interface AdmissionConnection {
|
|
99
|
+
remoteId(): string;
|
|
100
|
+
acceptBi(): Promise<BiStream>;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Handle one connection on the aster.admission ALPN.
|
|
104
|
+
*
|
|
105
|
+
* Runs the 6-step verification protocol:
|
|
106
|
+
* 1. Read token + attestation from consumer
|
|
107
|
+
* 2. Verify attestation against @aster root key
|
|
108
|
+
* 3. Verify token against attestation + service binding
|
|
109
|
+
* 4. Send challenge nonce
|
|
110
|
+
* 5. Read proof of possession
|
|
111
|
+
* 6. Verify proof, admit consumer
|
|
112
|
+
*/
|
|
113
|
+
export declare function handleDelegatedAdmissionConnection(conn: AdmissionConnection, opts: {
|
|
114
|
+
policy: DelegatedAdmissionPolicy;
|
|
115
|
+
hook?: MeshEndpointHook;
|
|
116
|
+
peerStore?: PeerAttributeStore;
|
|
117
|
+
}): Promise<void>;
|
|
118
|
+
//# sourceMappingURL=delegated.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegated.d.ts","sourceRoot":"","sources":["../../src/trust/delegated.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* aster/trust/delegated -- Delegated admission via @aster-issued enrollment tokens.
|
|
3
|
+
*
|
|
4
|
+
* Implements the `aster.admission` ALPN handler (Aster-trust-spec S3a).
|
|
5
|
+
*
|
|
6
|
+
* Protocol:
|
|
7
|
+
* 1. Consumer sends: EnrollmentToken (JSON) + SigningKeyAttestation (JSON)
|
|
8
|
+
* 2. Service verifies: attestation -> token -> service binding
|
|
9
|
+
* 3. Service sends: AdmissionChallenge (32-byte nonce + service identity)
|
|
10
|
+
* 4. Consumer sends: AdmissionProof (signature over challenge with root key)
|
|
11
|
+
* 5. Service verifies: proof of possession
|
|
12
|
+
* 6. Service admits with: {handle, roles}
|
|
13
|
+
*
|
|
14
|
+
* All crypto is ed25519. No network calls -- verification uses cached
|
|
15
|
+
* attestations and the @aster root pubkey received at publish time.
|
|
16
|
+
*/
|
|
17
|
+
import { RpcError, StatusCode } from '../status.js';
|
|
18
|
+
import { verify, hexToBytes, bytesToHex, loadPublicKey, ATTR_ROLE } from './credentials.js';
|
|
19
|
+
import { createPeerAdmission } from '../peer-store.js';
|
|
20
|
+
// ── Canonical JSON for signature verification ──────────────────────────────
|
|
21
|
+
const encoder = new TextEncoder();
|
|
22
|
+
/** Deterministic JSON encoding for signature verification. */
|
|
23
|
+
function canonicalJsonBytes(obj) {
|
|
24
|
+
const sorted = Object.keys(obj).sort();
|
|
25
|
+
const ordered = {};
|
|
26
|
+
for (const k of sorted)
|
|
27
|
+
ordered[k] = obj[k];
|
|
28
|
+
return encoder.encode(JSON.stringify(ordered));
|
|
29
|
+
}
|
|
30
|
+
// ── Verification functions ─────────────────────────────────────────────────
|
|
31
|
+
/**
|
|
32
|
+
* Verify a signing-key attestation against the @aster root pubkey.
|
|
33
|
+
* Throws RpcError on failure.
|
|
34
|
+
*/
|
|
35
|
+
export async function verifyAttestation(attestation, opts) {
|
|
36
|
+
const now = opts.now ?? Math.floor(Date.now() / 1000);
|
|
37
|
+
const payload = {
|
|
38
|
+
signing_pubkey: attestation.signingPubkey,
|
|
39
|
+
key_id: attestation.keyId,
|
|
40
|
+
valid_from: attestation.validFrom,
|
|
41
|
+
valid_until: attestation.validUntil,
|
|
42
|
+
};
|
|
43
|
+
try {
|
|
44
|
+
const rootPubkey = loadPublicKey(hexToBytes(opts.asterRootPubkeyHex));
|
|
45
|
+
const valid = await verify(rootPubkey, canonicalJsonBytes(payload), hexToBytes(attestation.rootSignature));
|
|
46
|
+
if (!valid) {
|
|
47
|
+
throw new Error('signature invalid');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (cause) {
|
|
51
|
+
throw new RpcError(StatusCode.UNAUTHENTICATED, 'signing-key attestation verification failed');
|
|
52
|
+
}
|
|
53
|
+
if (!(attestation.validFrom <= now && now <= attestation.validUntil)) {
|
|
54
|
+
throw new RpcError(StatusCode.UNAUTHENTICATED, 'signing-key attestation is not currently valid');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Verify an enrollment token against the attestation and service policy.
|
|
59
|
+
* Throws RpcError on failure.
|
|
60
|
+
*/
|
|
61
|
+
export async function verifyToken(token, attestation, opts) {
|
|
62
|
+
const now = opts.now ?? Math.floor(Date.now() / 1000);
|
|
63
|
+
// Token must reference the correct signing key
|
|
64
|
+
if (token.signingKeyId !== attestation.keyId) {
|
|
65
|
+
throw new RpcError(StatusCode.UNAUTHENTICATED, 'token signing key does not match attestation');
|
|
66
|
+
}
|
|
67
|
+
// Verify token signature against the attested signing key
|
|
68
|
+
const tokenPayload = {
|
|
69
|
+
consumer_handle: token.consumerHandle,
|
|
70
|
+
consumer_pubkey: token.consumerPubkey,
|
|
71
|
+
target_handle: token.targetHandle,
|
|
72
|
+
target_service: token.targetService,
|
|
73
|
+
target_contract_id: token.targetContractId,
|
|
74
|
+
roles: token.roles,
|
|
75
|
+
issued_at: token.issuedAt,
|
|
76
|
+
expires_at: token.expiresAt,
|
|
77
|
+
signing_key_id: token.signingKeyId,
|
|
78
|
+
};
|
|
79
|
+
try {
|
|
80
|
+
const signingPubkey = loadPublicKey(hexToBytes(attestation.signingPubkey));
|
|
81
|
+
const valid = await verify(signingPubkey, canonicalJsonBytes(tokenPayload), hexToBytes(token.signature));
|
|
82
|
+
if (!valid) {
|
|
83
|
+
throw new Error('signature invalid');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (cause) {
|
|
87
|
+
if (cause instanceof RpcError)
|
|
88
|
+
throw cause;
|
|
89
|
+
throw new RpcError(StatusCode.UNAUTHENTICATED, 'enrollment token signature verification failed');
|
|
90
|
+
}
|
|
91
|
+
// Token expiry
|
|
92
|
+
if (!(token.issuedAt <= now && now <= token.expiresAt)) {
|
|
93
|
+
throw new RpcError(StatusCode.UNAUTHENTICATED, 'enrollment token has expired');
|
|
94
|
+
}
|
|
95
|
+
// Service binding -- all three must match
|
|
96
|
+
if (token.targetHandle !== opts.policy.targetHandle) {
|
|
97
|
+
throw new RpcError(StatusCode.PERMISSION_DENIED, 'token targets a different handle');
|
|
98
|
+
}
|
|
99
|
+
if (token.targetService !== opts.policy.targetService) {
|
|
100
|
+
throw new RpcError(StatusCode.PERMISSION_DENIED, 'token targets a different service');
|
|
101
|
+
}
|
|
102
|
+
if (token.targetContractId !== opts.policy.targetContractId) {
|
|
103
|
+
throw new RpcError(StatusCode.PERMISSION_DENIED, 'token targets a different contract');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Verify the consumer's proof of possession of their root key.
|
|
108
|
+
* Throws RpcError on failure.
|
|
109
|
+
*/
|
|
110
|
+
export async function verifyProofOfPossession(opts) {
|
|
111
|
+
try {
|
|
112
|
+
const consumerPubkey = loadPublicKey(hexToBytes(opts.consumerPubkeyHex));
|
|
113
|
+
const valid = await verify(consumerPubkey, opts.challengeBytes, hexToBytes(opts.signatureHex));
|
|
114
|
+
if (!valid) {
|
|
115
|
+
throw new Error('signature invalid');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (cause) {
|
|
119
|
+
if (cause instanceof RpcError)
|
|
120
|
+
throw cause;
|
|
121
|
+
throw new RpcError(StatusCode.UNAUTHENTICATED, 'admission proof verification failed');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** Build the challenge payload that the consumer must sign. */
|
|
125
|
+
export function buildChallengeBytes(nonce, targetHandle, targetService, alpn = 'aster.admission') {
|
|
126
|
+
const parts = [
|
|
127
|
+
nonce,
|
|
128
|
+
encoder.encode(targetHandle),
|
|
129
|
+
encoder.encode(targetService),
|
|
130
|
+
encoder.encode(alpn),
|
|
131
|
+
];
|
|
132
|
+
const totalLen = parts.reduce((sum, p) => sum + p.length, 0);
|
|
133
|
+
const result = new Uint8Array(totalLen);
|
|
134
|
+
let offset = 0;
|
|
135
|
+
for (const p of parts) {
|
|
136
|
+
result.set(p, offset);
|
|
137
|
+
offset += p.length;
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Handle one connection on the aster.admission ALPN.
|
|
143
|
+
*
|
|
144
|
+
* Runs the 6-step verification protocol:
|
|
145
|
+
* 1. Read token + attestation from consumer
|
|
146
|
+
* 2. Verify attestation against @aster root key
|
|
147
|
+
* 3. Verify token against attestation + service binding
|
|
148
|
+
* 4. Send challenge nonce
|
|
149
|
+
* 5. Read proof of possession
|
|
150
|
+
* 6. Verify proof, admit consumer
|
|
151
|
+
*/
|
|
152
|
+
export async function handleDelegatedAdmissionConnection(conn, opts) {
|
|
153
|
+
const peerId = conn.remoteId();
|
|
154
|
+
const now = Math.floor(Date.now() / 1000);
|
|
155
|
+
try {
|
|
156
|
+
const { send, recv } = await conn.acceptBi();
|
|
157
|
+
// Step 1: Read token + attestation
|
|
158
|
+
const raw = await recv.readToEnd(64 * 1024);
|
|
159
|
+
if (!raw || raw.length === 0) {
|
|
160
|
+
console.warn(`delegated admission: empty request from ${peerId}`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
let request;
|
|
164
|
+
try {
|
|
165
|
+
request = JSON.parse(new TextDecoder().decode(raw));
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
console.warn(`delegated admission: malformed JSON from ${peerId}`);
|
|
169
|
+
await sendReject(send, 'malformed request');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
// Parse token and attestation from wire format (snake_case) to TS (camelCase)
|
|
173
|
+
const tokenData = request.token ?? {};
|
|
174
|
+
const attData = request.attestation ?? {};
|
|
175
|
+
const token = {
|
|
176
|
+
consumerHandle: tokenData.consumer_handle ?? '',
|
|
177
|
+
consumerPubkey: tokenData.consumer_pubkey ?? '',
|
|
178
|
+
targetHandle: tokenData.target_handle ?? '',
|
|
179
|
+
targetService: tokenData.target_service ?? '',
|
|
180
|
+
targetContractId: tokenData.target_contract_id ?? '',
|
|
181
|
+
roles: tokenData.roles ?? [],
|
|
182
|
+
issuedAt: tokenData.issued_at ?? 0,
|
|
183
|
+
expiresAt: tokenData.expires_at ?? 0,
|
|
184
|
+
signingKeyId: tokenData.signing_key_id ?? '',
|
|
185
|
+
signature: tokenData.signature ?? '',
|
|
186
|
+
};
|
|
187
|
+
const attestation = {
|
|
188
|
+
signingPubkey: attData.signing_pubkey ?? '',
|
|
189
|
+
keyId: attData.key_id ?? '',
|
|
190
|
+
validFrom: attData.valid_from ?? 0,
|
|
191
|
+
validUntil: attData.valid_until ?? 0,
|
|
192
|
+
rootSignature: attData.root_signature ?? '',
|
|
193
|
+
};
|
|
194
|
+
// Steps 2-3: Verify attestation and token
|
|
195
|
+
try {
|
|
196
|
+
await verifyAttestation(attestation, {
|
|
197
|
+
asterRootPubkeyHex: opts.policy.asterRootPubkey,
|
|
198
|
+
now,
|
|
199
|
+
});
|
|
200
|
+
await verifyToken(token, attestation, { policy: opts.policy, now });
|
|
201
|
+
}
|
|
202
|
+
catch (e) {
|
|
203
|
+
if (e instanceof RpcError) {
|
|
204
|
+
console.info(`delegated admission: denied ${peerId}: ${e.message}`);
|
|
205
|
+
await sendReject(send, e.message);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
throw e;
|
|
209
|
+
}
|
|
210
|
+
// Step 4: Send challenge
|
|
211
|
+
const nonce = crypto.getRandomValues(new Uint8Array(32));
|
|
212
|
+
const challenge = {
|
|
213
|
+
nonce: bytesToHex(nonce),
|
|
214
|
+
target_handle: opts.policy.targetHandle,
|
|
215
|
+
target_service: opts.policy.targetService,
|
|
216
|
+
};
|
|
217
|
+
await send.writeAll(encoder.encode(JSON.stringify(challenge)));
|
|
218
|
+
// Step 5: Read proof
|
|
219
|
+
const proofRaw = await recv.readToEnd(4096);
|
|
220
|
+
if (!proofRaw || proofRaw.length === 0) {
|
|
221
|
+
console.warn(`delegated admission: no proof from ${peerId}`);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
let proof;
|
|
225
|
+
try {
|
|
226
|
+
proof = JSON.parse(new TextDecoder().decode(proofRaw));
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
await sendReject(send, 'malformed proof');
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
// Step 6: Verify proof of possession
|
|
233
|
+
const challengeBytes = buildChallengeBytes(nonce, opts.policy.targetHandle, opts.policy.targetService);
|
|
234
|
+
try {
|
|
235
|
+
await verifyProofOfPossession({
|
|
236
|
+
consumerPubkeyHex: token.consumerPubkey,
|
|
237
|
+
challengeBytes,
|
|
238
|
+
signatureHex: proof.signature ?? '',
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
catch (e) {
|
|
242
|
+
if (e instanceof RpcError) {
|
|
243
|
+
console.info(`delegated admission: proof failed ${peerId}: ${e.message}`);
|
|
244
|
+
await sendReject(send, e.message);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
throw e;
|
|
248
|
+
}
|
|
249
|
+
// Admitted!
|
|
250
|
+
console.info(`delegated admission: admitted ${peerId} (handle=${token.consumerHandle}, roles=${token.roles.join(',')})`);
|
|
251
|
+
// Store admission attributes
|
|
252
|
+
if (opts.peerStore) {
|
|
253
|
+
opts.peerStore.admit(createPeerAdmission({
|
|
254
|
+
endpointId: peerId,
|
|
255
|
+
handle: token.consumerHandle,
|
|
256
|
+
attributes: new Map([[ATTR_ROLE, token.roles.join(',')]]),
|
|
257
|
+
admissionPath: 'aster.admission',
|
|
258
|
+
}));
|
|
259
|
+
}
|
|
260
|
+
// Add to Gate 0 allowlist
|
|
261
|
+
if (opts.hook) {
|
|
262
|
+
opts.hook.addPeer(peerId);
|
|
263
|
+
}
|
|
264
|
+
// Send success response
|
|
265
|
+
const result = {
|
|
266
|
+
admitted: true,
|
|
267
|
+
handle: token.consumerHandle,
|
|
268
|
+
roles: token.roles,
|
|
269
|
+
};
|
|
270
|
+
await send.writeAll(encoder.encode(JSON.stringify(result)));
|
|
271
|
+
await send.finish();
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
// Cancellation -- silently exit
|
|
275
|
+
if (err instanceof Error && (err.name === 'AbortError' || err.message.includes('cancel'))) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
console.error(`delegated admission: error for ${peerId}:`, err);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/** Send a rejection response and finish the stream. */
|
|
282
|
+
async function sendReject(send, reason) {
|
|
283
|
+
try {
|
|
284
|
+
const result = JSON.stringify({ admitted: false, reason });
|
|
285
|
+
await send.writeAll(encoder.encode(result));
|
|
286
|
+
await send.finish();
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
// Best-effort
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=delegated.js.map
|