@thirtified/eudi-dev 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +223 -2
  3. package/dist/binary.d.ts +42 -0
  4. package/dist/binary.js +76 -0
  5. package/dist/binary.js.map +1 -0
  6. package/dist/commands/credentials.d.ts +151 -0
  7. package/dist/commands/credentials.js +234 -0
  8. package/dist/commands/credentials.js.map +1 -0
  9. package/dist/commands/registrar.d.ts +116 -0
  10. package/dist/commands/registrar.js +150 -0
  11. package/dist/commands/registrar.js.map +1 -0
  12. package/dist/commands/trust.d.ts +58 -0
  13. package/dist/commands/trust.js +90 -0
  14. package/dist/commands/trust.js.map +1 -0
  15. package/dist/errors.d.ts +47 -0
  16. package/dist/errors.js +41 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/eudi.d.ts +66 -0
  19. package/dist/eudi.js +113 -0
  20. package/dist/eudi.js.map +1 -0
  21. package/dist/index.d.ts +10 -0
  22. package/dist/index.js +8 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/input.d.ts +32 -0
  25. package/dist/input.js +7 -0
  26. package/dist/input.js.map +1 -0
  27. package/dist/runner.d.ts +86 -0
  28. package/dist/runner.js +246 -0
  29. package/dist/runner.js.map +1 -0
  30. package/dist/types.d.ts +345 -0
  31. package/dist/types.js +11 -0
  32. package/dist/types.js.map +1 -0
  33. package/dist/version.d.ts +24 -0
  34. package/dist/version.js +35 -0
  35. package/dist/version.js.map +1 -0
  36. package/package.json +71 -4
  37. package/src/binary.ts +114 -0
  38. package/src/commands/credentials.ts +384 -0
  39. package/src/commands/registrar.ts +243 -0
  40. package/src/commands/trust.ts +123 -0
  41. package/src/errors.ts +66 -0
  42. package/src/eudi.ts +159 -0
  43. package/src/index.ts +44 -0
  44. package/src/input.ts +27 -0
  45. package/src/runner.ts +310 -0
  46. package/src/types.ts +396 -0
  47. package/src/version.ts +36 -0
package/src/runner.ts ADDED
@@ -0,0 +1,310 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { mkdtemp, rm, stat, writeFile } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+
6
+ import { EudiDevError, lastLine } from './errors.js';
7
+ import { type FileInput, type Input, isPathInput, isUrlInput } from './input.js';
8
+
9
+ export interface RunOptions {
10
+ /** Written to the process's stdin. Without it stdin is closed (/dev/null). */
11
+ stdin?: string | Uint8Array | undefined;
12
+ /** Kill the process after this many milliseconds. Default: the runner's timeout. */
13
+ timeoutMs?: number | undefined;
14
+ /** Aborts the process. */
15
+ signal?: AbortSignal | undefined;
16
+ /** Add `--json` and parse stdout as one JSON document. Default `true`. */
17
+ json?: boolean | undefined;
18
+ }
19
+
20
+ export interface RunResult<T = unknown> {
21
+ /** The parsed stdout document (`undefined` with `json: false` or empty stdout). */
22
+ document: T;
23
+ stdout: string;
24
+ /** Progress, hints and warnings. With `--json` nothing else goes to stdout. */
25
+ stderr: string;
26
+ }
27
+
28
+ export interface RunnerConfig {
29
+ bin: string;
30
+ /** Becomes EUDI_DEV_HOME. */
31
+ home: string;
32
+ /** Extra environment for every call. `undefined` removes a variable. */
33
+ env?: Record<string, string | undefined> | undefined;
34
+ /**
35
+ * Pass the caller's EUDI_DEV_* variables through to the binary. Default
36
+ * `false`: they are removed so that a developer's shell setup
37
+ * (EUDI_DEV_STORAGE, EUDI_DEV_SEED, ...) can't change test results.
38
+ */
39
+ inheritEudiEnv?: boolean | undefined;
40
+ timeoutMs?: number | undefined;
41
+ cwd?: string | undefined;
42
+ /** Upper bound for captured stdout plus stderr. Default 64 MiB. */
43
+ maxBufferBytes?: number | undefined;
44
+ }
45
+
46
+ const STRIPPED_LEGACY_VARS = ['OID4VC_DEV_HOME', 'EUDI_REMOTE_TIMEOUT'];
47
+
48
+ /** Builds the environment for a child process. Exported for tests. */
49
+ export function buildEnv(
50
+ config: Pick<RunnerConfig, 'home' | 'env' | 'inheritEudiEnv'>,
51
+ base: NodeJS.ProcessEnv = process.env,
52
+ ): NodeJS.ProcessEnv {
53
+ const env: NodeJS.ProcessEnv = {};
54
+ for (const [key, value] of Object.entries(base)) {
55
+ if (value === undefined) continue;
56
+ if (!config.inheritEudiEnv && (key.startsWith('EUDI_DEV_') || STRIPPED_LEGACY_VARS.includes(key))) continue;
57
+ env[key] = value;
58
+ }
59
+ env.EUDI_DEV_HOME = config.home;
60
+ env.NO_COLOR = '1';
61
+ for (const [key, value] of Object.entries(config.env ?? {})) {
62
+ if (value === undefined) delete env[key];
63
+ else env[key] = value;
64
+ }
65
+ return env;
66
+ }
67
+
68
+ /** Collects flags as `--name=value`, so a value starting with `-` is never read as a flag. */
69
+ export class ArgList {
70
+ readonly args: string[];
71
+
72
+ constructor(...command: string[]) {
73
+ this.args = [...command];
74
+ }
75
+
76
+ /** `true` → `--name`, `false` → `--name=false`, `undefined` → nothing. */
77
+ bool(name: string, value: boolean | undefined): this {
78
+ if (value === true) this.args.push(`--${name}`);
79
+ else if (value === false) this.args.push(`--${name}=false`);
80
+ return this;
81
+ }
82
+
83
+ /** Only adds `--name` when `value` is true. For flags that default to false. */
84
+ on(name: string, value: boolean | undefined): this {
85
+ if (value) this.args.push(`--${name}`);
86
+ return this;
87
+ }
88
+
89
+ value(name: string, value: string | number | undefined | null): this {
90
+ if (value !== undefined && value !== null) this.args.push(`--${name}=${value}`);
91
+ return this;
92
+ }
93
+
94
+ /** A repeatable flag: one `--name=value` per entry. */
95
+ repeat(name: string, values: readonly (string | number)[] | undefined): this {
96
+ for (const v of values ?? []) this.args.push(`--${name}=${v}`);
97
+ return this;
98
+ }
99
+
100
+ /** A comma-separated single flag. */
101
+ csv(name: string, values: readonly string[] | undefined): this {
102
+ if (values && values.length > 0) this.args.push(`--${name}=${values.join(',')}`);
103
+ return this;
104
+ }
105
+
106
+ push(...args: string[]): this {
107
+ this.args.push(...args);
108
+ return this;
109
+ }
110
+ }
111
+
112
+ /** Temp files for one call. */
113
+ export class Scratch {
114
+ private dir: string | undefined;
115
+ private n = 0;
116
+
117
+ /** Writes `content` to a private temp file and returns its path. */
118
+ async write(name: string, content: string | Uint8Array): Promise<string> {
119
+ this.dir ??= await mkdtemp(join(tmpdir(), 'eudi-dev-input-'));
120
+ const path = join(this.dir, `${++this.n}-${name}`);
121
+ await writeFile(path, content, { mode: 0o600 });
122
+ return path;
123
+ }
124
+
125
+ /**
126
+ * Turns a file-valued option into a flag value. Content is written to a temp
127
+ * file. `{ url }` is only allowed when `allowUrl` is set.
128
+ */
129
+ async file(input: FileInput, name: string, opts: { allowUrl?: boolean } = {}): Promise<string> {
130
+ if (isPathInput(input)) {
131
+ await assertFile(input.path);
132
+ return input.path;
133
+ }
134
+ if (isUrlInput(input)) {
135
+ if (!opts.allowUrl) throw new TypeError(`${name} does not accept a URL`);
136
+ assertHttpUrl(input.url);
137
+ return input.url;
138
+ }
139
+ return this.write(name, input);
140
+ }
141
+
142
+ async cleanup(): Promise<void> {
143
+ if (this.dir) await rm(this.dir, { recursive: true, force: true });
144
+ this.dir = undefined;
145
+ }
146
+ }
147
+
148
+ async function assertFile(path: string): Promise<void> {
149
+ let ok = false;
150
+ try {
151
+ ok = (await stat(path)).isFile();
152
+ } catch {
153
+ ok = false;
154
+ }
155
+ // eudi-dev treats a path that doesn't exist as raw input, which hides typos.
156
+ if (!ok) throw new TypeError(`${path} is not a file`);
157
+ }
158
+
159
+ function assertHttpUrl(url: string): void {
160
+ if (!/^https?:\/\//i.test(url)) throw new TypeError(`${url} is not an http(s) URL`);
161
+ }
162
+
163
+ /** Maps an {@link Input} to the positional argument and the stdin content. */
164
+ export async function positionalInput(input: Input): Promise<{ arg: string; stdin?: string | Uint8Array }> {
165
+ if (isPathInput(input)) {
166
+ await assertFile(input.path);
167
+ return { arg: input.path };
168
+ }
169
+ if (isUrlInput(input)) {
170
+ assertHttpUrl(input.url);
171
+ return { arg: input.url };
172
+ }
173
+ if (typeof input !== 'string' && !(input instanceof Uint8Array)) {
174
+ throw new TypeError('input must be a string, bytes, { path } or { url }');
175
+ }
176
+ return { arg: '-', stdin: input };
177
+ }
178
+
179
+ /** Spawns the eudi-dev binary with an isolated environment. */
180
+ export class Runner {
181
+ readonly bin: string;
182
+ readonly home: string;
183
+ private readonly config: RunnerConfig;
184
+
185
+ constructor(config: RunnerConfig) {
186
+ this.config = config;
187
+ this.bin = config.bin;
188
+ this.home = config.home;
189
+ }
190
+
191
+ /**
192
+ * Runs `eudi <args> --json --no-color` and parses stdout as exactly one
193
+ * JSON document. Rejects with {@link EudiDevError} on a non-zero exit, a
194
+ * timeout, or stdout that is not one JSON document.
195
+ */
196
+ async run<T = unknown>(args: readonly string[], opts: RunOptions = {}): Promise<RunResult<T>> {
197
+ const json = opts.json ?? true;
198
+ const fullArgs = json ? [...args, '--json', '--no-color'] : [...args, '--no-color'];
199
+ const { exitCode, signal, stdout, stderr, timedOut, spawnError } = await this.spawn(fullArgs, opts);
200
+
201
+ const base = { args: fullArgs, exitCode, signal, stdout, stderr };
202
+ if (spawnError) {
203
+ const message =
204
+ spawnError.name === 'AbortError' ? `eudi ${args[0] ?? ''} was aborted` : `Could not run ${this.bin}: ${spawnError.message}`;
205
+ throw new EudiDevError(message, { ...base, cause: spawnError });
206
+ }
207
+
208
+ let document: unknown;
209
+ let parseError: unknown;
210
+ if (json && stdout.trim() !== '') {
211
+ try {
212
+ document = JSON.parse(stdout);
213
+ } catch (err) {
214
+ parseError = err;
215
+ }
216
+ }
217
+
218
+ if (timedOut) {
219
+ throw new EudiDevError(`eudi ${args[0] ?? ''} timed out`, { ...base, document, timedOut: true });
220
+ }
221
+ if (exitCode !== 0) {
222
+ const reason = lastLine(stderr) || (signal ? `killed by ${signal}` : `exit code ${exitCode}`);
223
+ throw new EudiDevError(reason, { ...base, document });
224
+ }
225
+ if (parseError) {
226
+ throw new EudiDevError(`eudi ${args.join(' ')} printed stdout that is not one JSON document`, {
227
+ ...base,
228
+ cause: parseError,
229
+ });
230
+ }
231
+ return { document: document as T, stdout, stderr };
232
+ }
233
+
234
+ private spawn(
235
+ args: string[],
236
+ opts: RunOptions,
237
+ ): Promise<{
238
+ exitCode: number | null;
239
+ signal: NodeJS.Signals | null;
240
+ stdout: string;
241
+ stderr: string;
242
+ timedOut: boolean;
243
+ spawnError?: Error;
244
+ }> {
245
+ const maxBytes = this.config.maxBufferBytes ?? 64 * 1024 * 1024;
246
+ const timeoutMs = opts.timeoutMs ?? this.config.timeoutMs ?? 60_000;
247
+
248
+ return new Promise((resolve) => {
249
+ const child = spawn(this.bin, args, {
250
+ cwd: this.config.cwd,
251
+ env: buildEnv(this.config),
252
+ stdio: [opts.stdin !== undefined ? 'pipe' : 'ignore', 'pipe', 'pipe'],
253
+ windowsHide: true,
254
+ ...(opts.signal ? { signal: opts.signal } : {}),
255
+ });
256
+
257
+ const out: Buffer[] = [];
258
+ const err: Buffer[] = [];
259
+ let bytes = 0;
260
+ let timedOut = false;
261
+ let overflow = false;
262
+ let spawnError: Error | undefined;
263
+
264
+ const collect = (sink: Buffer[]) => (chunk: Buffer) => {
265
+ bytes += chunk.length;
266
+ if (bytes > maxBytes) {
267
+ if (!overflow) {
268
+ overflow = true;
269
+ child.kill('SIGKILL');
270
+ }
271
+ return;
272
+ }
273
+ sink.push(chunk);
274
+ };
275
+ child.stdout?.on('data', collect(out));
276
+ child.stderr?.on('data', collect(err));
277
+
278
+ const timer =
279
+ timeoutMs > 0
280
+ ? setTimeout(() => {
281
+ timedOut = true;
282
+ child.kill('SIGKILL');
283
+ }, timeoutMs)
284
+ : undefined;
285
+
286
+ child.on('error', (e) => {
287
+ spawnError = e;
288
+ });
289
+ child.on('close', (code, signal) => {
290
+ if (timer) clearTimeout(timer);
291
+ let stderr = Buffer.concat(err).toString('utf8');
292
+ if (overflow) stderr += `\n[eudi-dev] output exceeded ${maxBytes} bytes, process killed`;
293
+ const result = {
294
+ exitCode: code,
295
+ signal,
296
+ stdout: Buffer.concat(out).toString('utf8'),
297
+ stderr,
298
+ timedOut,
299
+ };
300
+ resolve(spawnError ? { ...result, spawnError } : result);
301
+ });
302
+
303
+ if (opts.stdin !== undefined && child.stdin) {
304
+ // The process may exit before it reads stdin (for example on a flag error).
305
+ child.stdin.on('error', () => {});
306
+ child.stdin.end(opts.stdin);
307
+ }
308
+ });
309
+ }
310
+ }
package/src/types.ts ADDED
@@ -0,0 +1,396 @@
1
+ /**
2
+ * Types for the `--json` documents of eudi-dev v3.
3
+ *
4
+ * Written by hand from the Go structs and JSON builders at v3.10.1
5
+ * (internal/output, internal/validate, internal/statuslist, internal/registrar,
6
+ * internal/trustedlists, internal/certinfo). Upstream has no OpenAPI or JSON
7
+ * schema for these documents. Within v3 fields are only added (ADR 0020), so
8
+ * every object type also accepts unknown keys.
9
+ */
10
+
11
+ /** A JSON object whose shape this package does not pin down. */
12
+ export type JsonObject = { [key: string]: unknown };
13
+
14
+ /** Lets an object carry fields added by later v3 releases. */
15
+ type Open = { [key: string]: unknown };
16
+
17
+ /**
18
+ * Like `Partial<T>`, but also accepts an explicit `undefined` under
19
+ * `exactOptionalPropertyTypes`. Used for objects that callers pass in.
20
+ */
21
+ export type Loose<T> = { [K in keyof T]?: T[K] | undefined };
22
+
23
+ // ── Checks ────────────────────────────────────────────────────────────────
24
+
25
+ export type CheckStatus = 'pass' | 'fail' | 'warning' | 'skipped';
26
+
27
+ /** Check names, in the order `validate` runs them. */
28
+ export type CheckName =
29
+ | 'type'
30
+ | 'expiry'
31
+ | 'integrity'
32
+ | 'trusted list'
33
+ | 'signature'
34
+ | 'trust'
35
+ | 'status'
36
+ | 'certificate revocation'
37
+ | 'status list signature'
38
+ | 'haip'
39
+ | (string & {});
40
+
41
+ /** The trusted list service behind a passing trust or status list signature check. */
42
+ export interface TrustAnchor extends Open {
43
+ entry?: string;
44
+ list_url: string;
45
+ service: string;
46
+ }
47
+
48
+ export interface Check extends Open {
49
+ name: CheckName;
50
+ status: CheckStatus;
51
+ detail: string;
52
+ /** The check was skipped because the validation ran offline. */
53
+ needsNetwork?: boolean;
54
+ anchor?: TrustAnchor;
55
+ }
56
+
57
+ // ── Certificates ──────────────────────────────────────────────────────────
58
+
59
+ export interface CertificateExtension extends Open {
60
+ oid?: string;
61
+ name?: string;
62
+ critical?: boolean;
63
+ value?: unknown;
64
+ }
65
+
66
+ export interface CertificateInfo extends Open {
67
+ subject: string;
68
+ issuer: string;
69
+ serialNumber: string;
70
+ /** RFC 3339 time. */
71
+ notBefore: string;
72
+ /** RFC 3339 time. */
73
+ notAfter: string;
74
+ selfSigned: boolean;
75
+ ca: boolean;
76
+ maxPathLength?: number;
77
+ publicKey: string;
78
+ signatureAlgorithm: string;
79
+ keyUsage?: string[];
80
+ extendedKeyUsage?: string[];
81
+ subjectAltNames?: string[];
82
+ policies?: string[];
83
+ crlDistributionPoints?: string[];
84
+ ocspServers?: string[];
85
+ issuingCertificateURLs?: string[];
86
+ subjectKeyId?: string;
87
+ authorityKeyId?: string;
88
+ extensions: CertificateExtension[];
89
+ sha256Fingerprint: string;
90
+ pem: string;
91
+ }
92
+
93
+ // ── Decoded credentials ───────────────────────────────────────────────────
94
+
95
+ export interface Disclosure extends Open {
96
+ name: string;
97
+ value: unknown;
98
+ salt: string;
99
+ digest: string;
100
+ isArrayEntry: boolean;
101
+ /** False for a disclosure whose digest the credential does not reference. */
102
+ referenced: boolean;
103
+ }
104
+
105
+ interface DecodedBase extends Open {
106
+ /** Parser warnings. */
107
+ warnings?: string[];
108
+ /** Spec deviations (for example RFC 9901 §7.1 rejection rules). */
109
+ deviations?: string[];
110
+ /** The x5c (JWT) or x5chain (mdoc) certificates, leaf first. */
111
+ certificates?: CertificateInfo[];
112
+ }
113
+
114
+ export interface DecodedSdJwt extends DecodedBase {
115
+ format: 'dc+sd-jwt';
116
+ header: JsonObject;
117
+ payload: JsonObject;
118
+ disclosures: Disclosure[];
119
+ /** The payload with every disclosure applied. */
120
+ resolvedClaims: JsonObject;
121
+ keyBindingJWT?: { header: JsonObject; payload: JsonObject };
122
+ }
123
+
124
+ export interface DecodedJwt extends DecodedBase {
125
+ format: 'jwt';
126
+ header: JsonObject;
127
+ payload: JsonObject;
128
+ }
129
+
130
+ export interface MdocValidityInfo extends Open {
131
+ signed?: string;
132
+ validFrom?: string;
133
+ validUntil?: string;
134
+ }
135
+
136
+ export interface DecodedMdoc extends DecodedBase {
137
+ format: 'mso_mdoc';
138
+ docType: string;
139
+ /** namespace → element identifier → value. */
140
+ claims: Record<string, Record<string, unknown>>;
141
+ isDeviceResponse?: boolean;
142
+ responseVersion?: string;
143
+ responseStatus?: number;
144
+ mso?: {
145
+ version: string;
146
+ digestAlgorithm: string;
147
+ docType: string;
148
+ validityInfo?: MdocValidityInfo;
149
+ status?: unknown;
150
+ /** namespace → digest id → base64url digest. */
151
+ valueDigests?: Record<string, Record<string, string>>;
152
+ deviceKeyInfo?: unknown;
153
+ } & Open;
154
+ deviceKey?: unknown;
155
+ issuerAuth?: unknown;
156
+ }
157
+
158
+ export interface DecodedCertificates extends Open {
159
+ format: 'x509';
160
+ certificates: CertificateInfo[];
161
+ }
162
+
163
+ export type DecodedCredential = DecodedSdJwt | DecodedJwt | DecodedMdoc | DecodedCertificates;
164
+
165
+ /**
166
+ * `decode --json` of a credential or certificate. It runs the decoder's
167
+ * checks offline-capable, so the result carries `validation.checks`.
168
+ */
169
+ export type DecodeCredentialResult = DecodedCredential & { validation: { checks: Check[] } };
170
+
171
+ /**
172
+ * `decode --json` of an OpenID4VCI offer, an OpenID4VP request or a trusted
173
+ * list. Their documents are not typed by this package.
174
+ */
175
+ export type DecodeOtherResult = JsonObject;
176
+
177
+ export type DecodeResult = DecodeCredentialResult | DecodeOtherResult;
178
+
179
+ // ── validate ──────────────────────────────────────────────────────────────
180
+
181
+ export interface SignatureVerification extends Open {
182
+ signatureValid: boolean;
183
+ expired: boolean;
184
+ notYetValid: boolean;
185
+ algorithm?: string;
186
+ errors?: string[];
187
+ /** SD-JWT / JWT. */
188
+ keyId?: string;
189
+ issuer?: string;
190
+ expiresAt?: string;
191
+ issuedAt?: string;
192
+ notBefore?: string;
193
+ /** mdoc. */
194
+ docType?: string;
195
+ validFrom?: string;
196
+ validUntil?: string;
197
+ signed?: string;
198
+ warnings?: string[];
199
+ }
200
+
201
+ export interface StatusResult extends Open {
202
+ uri: string;
203
+ index: number;
204
+ status: number;
205
+ /** VALID, INVALID, SUSPENDED, an application specific value, or unknown. */
206
+ statusName: string;
207
+ isValid: boolean;
208
+ bitsPerEntry: number;
209
+ /** "jwt" or "cwt". */
210
+ format: string;
211
+ subject?: string;
212
+ signatureValid?: boolean;
213
+ trustAnchored: boolean;
214
+ warnings?: string[];
215
+ haipFindings?: string[];
216
+ }
217
+
218
+ export interface CatalogueTrust extends Open {
219
+ entry: string;
220
+ category: string;
221
+ anchored_by?: string;
222
+ findings?: string[];
223
+ warnings?: string[];
224
+ }
225
+
226
+ /** `validate --json`: the decoded credential plus the checks at top level. */
227
+ export type ValidateReport = DecodedCredential & {
228
+ checks: Check[];
229
+ verification?: SignatureVerification;
230
+ status?: StatusResult;
231
+ trust?: CatalogueTrust;
232
+ /** Present with `--haip` (empty when there are no findings). */
233
+ haipFindings?: string[];
234
+ };
235
+
236
+ // ── dcql ──────────────────────────────────────────────────────────────────
237
+
238
+ // Also an input (registrar add --dcql), so optional fields accept undefined.
239
+ export interface DcqlClaimQuery extends Open {
240
+ id?: string | undefined;
241
+ path: (string | number | null)[];
242
+ values?: unknown[] | undefined;
243
+ }
244
+
245
+ export interface DcqlCredentialQuery extends Open {
246
+ id: string;
247
+ format: 'dc+sd-jwt' | 'jwt_vc_json' | 'mso_mdoc' | (string & {});
248
+ meta?: ({ vct_values?: string[] | undefined; doctype_value?: string | undefined } & Open) | undefined;
249
+ claims?: DcqlClaimQuery[] | undefined;
250
+ claim_sets?: string[][] | undefined;
251
+ }
252
+
253
+ export interface DcqlQuery extends Open {
254
+ credentials: DcqlCredentialQuery[];
255
+ credential_sets?: unknown[] | undefined;
256
+ }
257
+
258
+ // ── issue ─────────────────────────────────────────────────────────────────
259
+
260
+ /** `issue <format> --json`. `id` is set only when the wallet stored the credential. */
261
+ export interface IssueDocument extends Open {
262
+ credential: string;
263
+ id?: string;
264
+ }
265
+
266
+ // ── version ───────────────────────────────────────────────────────────────
267
+
268
+ export interface VersionInfo extends Open {
269
+ name: string;
270
+ version: string;
271
+ }
272
+
273
+ // ── trust ─────────────────────────────────────────────────────────────────
274
+
275
+ export interface TrustEntity extends Open {
276
+ id: string;
277
+ list: string;
278
+ name: string;
279
+ certificates: unknown[];
280
+ }
281
+
282
+ export interface TrustListLink extends Open {
283
+ url: string;
284
+ configured?: boolean;
285
+ via?: string;
286
+ error?: string;
287
+ }
288
+
289
+ /** `trust list --json`. */
290
+ export interface TrustOverview extends Open {
291
+ entities: TrustEntity[];
292
+ entity_lists: string[];
293
+ lists: TrustListLink[];
294
+ lists_url: string;
295
+ relying_party_cas?: string[];
296
+ trusted_list_cas?: string[];
297
+ trust_lists: ({ id: string; default?: boolean; category?: string; path?: string } & Open)[];
298
+ }
299
+
300
+ /** A JWKS document as `trust ca-cert --jwks` prints it. */
301
+ export interface Jwks extends Open {
302
+ keys: JsonObject[];
303
+ }
304
+
305
+ // ── registrar ─────────────────────────────────────────────────────────────
306
+
307
+ export interface RegistrarIdentifier extends Open {
308
+ identifier: string;
309
+ type?: string;
310
+ }
311
+
312
+ export interface MultiLangString extends Open {
313
+ lang?: string;
314
+ content?: string;
315
+ }
316
+
317
+ export interface IntendedUse extends Open {
318
+ intendedUseIdentifier: string;
319
+ purpose: MultiLangString[];
320
+ privacyPolicy: unknown[];
321
+ createdAt: string;
322
+ credentials: unknown[];
323
+ }
324
+
325
+ export interface ProvidedAttestation extends Open {
326
+ format: string;
327
+ type: string;
328
+ }
329
+
330
+ export interface RelyingPartyService extends Open {
331
+ serviceTradeName: string;
332
+ serviceIdentifier?: string;
333
+ supportURI?: string;
334
+ srvDescription?: unknown;
335
+ entitlements: string[];
336
+ providesAttestations?: ProvidedAttestation[];
337
+ credentialIssuer?: string;
338
+ isIntermediary: boolean;
339
+ intendedUses?: IntendedUse[];
340
+ }
341
+
342
+ export interface RelyingParty extends Open {
343
+ identifier: RegistrarIdentifier[];
344
+ legalPerson: { legalName: string[] } & Open;
345
+ country: string;
346
+ tradeName?: string;
347
+ isPSB: boolean;
348
+ supervisoryAuthority: JsonObject;
349
+ registryURI: string;
350
+ services: RelyingPartyService[];
351
+ }
352
+
353
+ export interface RegistrationCertificateResult extends Open {
354
+ /** The bare registration certificate JWT (typ rc-wrp+jwt). */
355
+ registrationCertificate: string;
356
+ /** JSON array as a string, ready for a request's verifier_info parameter. */
357
+ verifierInfo?: string;
358
+ /** JSON array as a string, ready for the issuer metadata's issuer_info. */
359
+ issuerInfo?: string;
360
+ }
361
+
362
+ export interface AccessCertificateResult extends Open {
363
+ /** PEM leaf. */
364
+ certificate: string;
365
+ /** PEM leaf followed by the issuing CA. */
366
+ chain: string;
367
+ clientIds: string[];
368
+ }
369
+
370
+ export interface AccessCertificateRequest extends Open {
371
+ identifier?: string | undefined;
372
+ serviceIdentifier?: string | undefined;
373
+ csr: string;
374
+ dnsNames?: string[] | undefined;
375
+ validity?: string | undefined;
376
+ }
377
+
378
+ /** Input of `registrar enrol`, as for POST /api/registrar/enrolments. */
379
+ export interface Enrolment extends Open {
380
+ relyingParty: Loose<RelyingParty> & Open;
381
+ access?: AccessCertificateRequest | undefined;
382
+ registration?: JsonObject | undefined;
383
+ }
384
+
385
+ export interface EnrolmentResult extends Open {
386
+ relyingParty: RelyingParty;
387
+ access?: AccessCertificateResult;
388
+ registration?: RegistrationCertificateResult;
389
+ }
390
+
391
+ /** `registrar verifiers add --to` / `registrar issuers add --to`. */
392
+ export interface RegistrationExtensionResult extends Open {
393
+ relyingParty: RelyingParty;
394
+ intendedUse?: string;
395
+ registration: RegistrationCertificateResult | null;
396
+ }