@punica/editor 1.19.3 → 1.21.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@punica/editor",
3
- "version": "1.19.3",
3
+ "version": "1.21.0",
4
4
  "description": "Punica Editor",
5
5
  "private": false,
6
6
  "type": "module",
@@ -150,9 +150,20 @@ declare module 'punica' {
150
150
  getRecent(limit?: number): KernelEvent[];
151
151
 
152
152
  /**
153
- * Filter events by various criteria.
154
- * By default, only searches in-memory audit buffer.
155
- * Set includePersisted=true to also search disk-persisted events (may be slower).
153
+ * Filter events by various criteria. **In-memory audit buffer only** —
154
+ * the bounded ring, not the durable store.
155
+ *
156
+ * `includePersisted` is accepted and does nothing. Reading the store is
157
+ * async and this signature is not, so the flag was left as a placeholder
158
+ * and the doc claimed it worked; a consumer that trusted it got the ring
159
+ * and no indication of what it missed. Corrected 2026-08-28 after an
160
+ * evidence package would have reported a run as shorter than it was.
161
+ *
162
+ * To read the store, call `loadPersistedEvents` and merge — the two sets
163
+ * overlap and both are needed, because persistence batches at ten records
164
+ * or five seconds and a just-finished run is not on disk yet.
165
+ *
166
+ * @deprecated includePersisted — pass nothing; use `loadPersistedEvents`.
156
167
  */
157
168
  filterEvents(options?: {
158
169
  correlationId?: string;
@@ -212,6 +212,22 @@ declare module 'punica' {
212
212
  verifier: kernel.ApprovalTokenVerifier | undefined
213
213
  ) => void;
214
214
 
215
+ /**
216
+ * The signer and verifier currently in force — the injected pair, or
217
+ * the substrate's ephemeral default when a host installed none.
218
+ *
219
+ * Declared because the setters above do not work without them. The
220
+ * getter is the only reader of the module's active-signer binding, so
221
+ * with it unreachable the build eliminated the binding, and
222
+ * `setApprovalTokenSigner` compiled down to an empty function: a host
223
+ * could install a KMS signer and have it silently discarded, while
224
+ * `installDefaultApprovalTokenPair` installed only the verifier half.
225
+ * A seam is not reachable because a function exists; it is reachable
226
+ * when something published reads what it writes.
227
+ */
228
+ getApprovalTokenSigner: () => kernel.ApprovalTokenSigner;
229
+ getApprovalTokenVerifier: () => kernel.ApprovalTokenVerifier;
230
+
215
231
  /**
216
232
  * Who is at this workstation, so a user-initiated call's audit record
217
233
  * names a subject instead of leaving `actor.id` empty. Absent is a
@@ -219,6 +235,48 @@ declare module 'punica' {
219
235
  * invents nothing to fill it. Passing `undefined` clears it.
220
236
  */
221
237
  setHostIdentity: (identity: HostIdentity | undefined) => void;
238
+
239
+ /**
240
+ * Package signing. A host installs a signer during bootstrap so an
241
+ * evidence package can carry a signature a reader is able to check:
242
+ *
243
+ * punica.runtime.setPackageSigner(signer)
244
+ *
245
+ * **There is no substrate default, deliberately.** The approval-token
246
+ * seam above ships an ephemeral fallback, and a host that forgets to
247
+ * inject a stable signer still gets signatures — ones that stop
248
+ * verifying at the next launch, silently. A signature nobody can verify
249
+ * is worse than none, because it reads as one. With no signer installed
250
+ * `signing.sign` refuses and a package states that it is unsigned.
251
+ *
252
+ * Passing `undefined` clears it. The key belongs to the host and never
253
+ * to an extension: an extension that can reach the signing key can also
254
+ * produce the thing it signs.
255
+ */
256
+ setPackageSigner: (signer: PackageSigner | undefined) => void;
257
+
258
+ /**
259
+ * Is this installation able to sign at all? Answers without provoking
260
+ * the refusal, so a package builder can choose between producing an
261
+ * unsigned package and failing.
262
+ */
263
+ hasPackageSigner: () => boolean;
264
+
265
+ /**
266
+ * Verify a detached package signature. **Pure** — no host seam and no
267
+ * private key, so a machine that never produced the package (and could
268
+ * not have) still answers whether the bytes were altered. That is the
269
+ * property the asymmetric key is bought for.
270
+ *
271
+ * Returns `false` for every negative outcome, malformed input included:
272
+ * "could not check" and "checked and wrong" are the same answer to a
273
+ * caller, and throwing would tempt a consumer into reading the first as
274
+ * "checked and fine".
275
+ */
276
+ verifyPackageSignature: (
277
+ bytes: Uint8Array,
278
+ signature: PackageSignature
279
+ ) => Promise<boolean>;
222
280
  }
223
281
 
224
282
  /**
@@ -231,6 +289,54 @@ declare module 'punica' {
231
289
  displayName?: string | null;
232
290
  }
233
291
 
292
+ /**
293
+ * A detached signature over a package's bytes.
294
+ *
295
+ * Asymmetric on purpose. The substrate's other signer — the approval
296
+ * token — is HMAC over a shared secret, which can only be verified by
297
+ * whoever holds that secret, and whoever holds it could have produced the
298
+ * tag themselves. A package is meant to be handed to someone else, so it
299
+ * is signed with a key whose public half travels inside the signature and
300
+ * whose private half never leaves the host.
301
+ *
302
+ * **What it proves:** integrity — these bytes are the bytes that were
303
+ * signed. **What it does not prove:** authenticity — nothing vouches for
304
+ * the public key, because the machine that produces a package is the
305
+ * machine that signs it. `selfSigned` carries that limit so a consumer
306
+ * states it rather than implying more.
307
+ */
308
+ export interface PackageSignature {
309
+ /** ECDSA P-256 with SHA-256. Chosen over Ed25519 for WebCrypto reach. */
310
+ alg: 'ES256';
311
+ /** The public half, so any reader can verify without a shared secret. */
312
+ publicKeyJwk: JsonWebKey;
313
+ /** SHA-256 over the JWK's canonical coordinates, base64url. */
314
+ keyId: string;
315
+ /** The detached signature, base64url. */
316
+ signature: string;
317
+ /** When it was produced, epoch ms, as the signing host saw it. */
318
+ signedAt: number;
319
+ /** Always true today; integrity verifies, authenticity does not. */
320
+ selfSigned: true;
321
+ }
322
+
323
+ /**
324
+ * The host-injected half of package signing. Implemented by the host, not
325
+ * by the substrate and never by an extension.
326
+ */
327
+ export interface PackageSigner {
328
+ /**
329
+ * Sign `bytes`. The implementation prefixes a protocol context string
330
+ * before the key sees them, so a signature made here cannot be replayed
331
+ * as one made for a different protocol.
332
+ */
333
+ sign(bytes: Uint8Array): Promise<PackageSignature>;
334
+ /** The public key, for a package header written before signing. */
335
+ publicKey(): Promise<
336
+ Pick<PackageSignature, 'alg' | 'publicKeyJwk' | 'keyId'>
337
+ >;
338
+ }
339
+
234
340
  }
235
341
 
236
342
  export const runtime: runtime.RuntimeApi;
@@ -89,6 +89,61 @@ declare module 'punica' {
89
89
  signal?: AbortSignal;
90
90
  }
91
91
 
92
+ /**
93
+ * The closed vocabulary of lineage subject kinds.
94
+ *
95
+ * A capability schema declares a field a subject with the
96
+ * non-standard `subject: '<kind>'` keyword, and the gateway copies
97
+ * what it finds onto the audit record. That record is the only
98
+ * source an evidence package has for "which data did this run
99
+ * touch?", which is why the kinds are closed: a reader comparing
100
+ * two packages cannot interpret a word an author invented.
101
+ *
102
+ * `dataset` is a named body of data, `file` a workspace path whose
103
+ * contents a call read or wrote, `model` a model that produced
104
+ * something, `prompt` the configured behaviour it ran with,
105
+ * `collection` a vector index a retrieval read from, and `artifact`
106
+ * something the run produced.
107
+ */
108
+ export type SubjectKind =
109
+ | 'dataset'
110
+ | 'file'
111
+ | 'model'
112
+ | 'prompt'
113
+ | 'collection'
114
+ | 'artifact';
115
+
116
+ /**
117
+ * One thing a call touched, as recorded on the `capability.audited`
118
+ * event's payload under `subjects`.
119
+ *
120
+ * Absent rather than empty when the capability declares none, which
121
+ * is most of them: absence says nobody has decided what this
122
+ * capability's subjects are, where an empty array would say the call
123
+ * touched nothing.
124
+ *
125
+ * `from` matters and is not bookkeeping. On `llm.invoke` the prompt
126
+ * is an input (the caller names it, so it is known even when the
127
+ * call fails) and the model is an output (the instruction class
128
+ * picks it from its provider chain at call time), so a reader that
129
+ * ignored this field would read a requested model as a used one.
130
+ *
131
+ * `digest` is present only where a capability had a cheap way to
132
+ * produce one — a `dvc.lock` read hands back a digest beside each
133
+ * path, a file read does not, and hashing on every `fs.readFile`
134
+ * would put I/O in the audit path. Its absence downgrades the answer
135
+ * from integrity to identity; it does not invalidate it.
136
+ */
137
+ export interface CapabilitySubject {
138
+ kind: SubjectKind | string;
139
+ /** How it is named: a path, a model id, a collection. */
140
+ id: string;
141
+ /** Its content digest, when a sibling field declared one. */
142
+ digest?: string;
143
+ /** Which side of the call named it. */
144
+ from: 'input' | 'output';
145
+ }
146
+
92
147
  /**
93
148
  * Capability Gateway API
94
149
  */