@centient/secrets 0.9.0 → 0.11.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/README.md +424 -8
- package/dist/audit/hmac-chain.d.ts +117 -0
- package/dist/audit/hmac-chain.d.ts.map +1 -0
- package/dist/audit/hmac-chain.js +811 -0
- package/dist/audit/hmac-chain.js.map +1 -0
- package/dist/audit/ocsf.d.ts +108 -0
- package/dist/audit/ocsf.d.ts.map +1 -0
- package/dist/audit/ocsf.js +204 -0
- package/dist/audit/ocsf.js.map +1 -0
- package/dist/cli/hidden-prompt.d.ts.map +1 -1
- package/dist/cli/hidden-prompt.js +55 -10
- package/dist/cli/hidden-prompt.js.map +1 -1
- package/dist/cli/messages.d.ts +40 -8
- package/dist/cli/messages.d.ts.map +1 -1
- package/dist/cli/messages.js +34 -8
- package/dist/cli/messages.js.map +1 -1
- package/dist/cli/secrets-cli.d.ts.map +1 -1
- package/dist/cli/secrets-cli.js +191 -32
- package/dist/cli/secrets-cli.js.map +1 -1
- package/dist/client.d.ts +26 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +129 -0
- package/dist/client.js.map +1 -0
- package/dist/compatibility.d.ts +199 -0
- package/dist/compatibility.d.ts.map +1 -0
- package/dist/compatibility.js +419 -0
- package/dist/compatibility.js.map +1 -0
- package/dist/crypto/darwin-keychain.d.ts +394 -0
- package/dist/crypto/darwin-keychain.d.ts.map +1 -0
- package/dist/crypto/darwin-keychain.js +793 -0
- package/dist/crypto/darwin-keychain.js.map +1 -0
- package/dist/crypto/vault-common.d.ts +55 -12
- package/dist/crypto/vault-common.d.ts.map +1 -1
- package/dist/crypto/vault-common.js +74 -71
- package/dist/crypto/vault-common.js.map +1 -1
- package/dist/environment/EnvironmentManager.d.ts.map +1 -1
- package/dist/environment/EnvironmentManager.js +5 -10
- package/dist/environment/EnvironmentManager.js.map +1 -1
- package/dist/index.d.ts +37 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -6
- package/dist/index.js.map +1 -1
- package/dist/key-providers/keychain-provider.d.ts +30 -2
- package/dist/key-providers/keychain-provider.d.ts.map +1 -1
- package/dist/key-providers/keychain-provider.js +29 -5
- package/dist/key-providers/keychain-provider.js.map +1 -1
- package/dist/key-providers/onepassword-provider.d.ts +65 -5
- package/dist/key-providers/onepassword-provider.d.ts.map +1 -1
- package/dist/key-providers/onepassword-provider.js +159 -67
- package/dist/key-providers/onepassword-provider.js.map +1 -1
- package/dist/key-providers/op-cli.d.ts +78 -0
- package/dist/key-providers/op-cli.d.ts.map +1 -0
- package/dist/key-providers/op-cli.js +123 -0
- package/dist/key-providers/op-cli.js.map +1 -0
- package/dist/key-providers/passphrase-provider.d.ts +20 -0
- package/dist/key-providers/passphrase-provider.d.ts.map +1 -1
- package/dist/key-providers/passphrase-provider.js +49 -0
- package/dist/key-providers/passphrase-provider.js.map +1 -1
- package/dist/key-providers/probe.d.ts +46 -0
- package/dist/key-providers/probe.d.ts.map +1 -0
- package/dist/key-providers/probe.js +70 -0
- package/dist/key-providers/probe.js.map +1 -0
- package/dist/key-providers/types.d.ts +51 -1
- package/dist/key-providers/types.d.ts.map +1 -1
- package/dist/platform/agent-detect.d.ts +39 -3
- package/dist/platform/agent-detect.d.ts.map +1 -1
- package/dist/platform/agent-detect.js +55 -9
- package/dist/platform/agent-detect.js.map +1 -1
- package/dist/platform/session-context.d.ts +77 -0
- package/dist/platform/session-context.d.ts.map +1 -0
- package/dist/platform/session-context.js +83 -0
- package/dist/platform/session-context.js.map +1 -0
- package/dist/policies/audit-trail.d.ts +25 -0
- package/dist/policies/audit-trail.d.ts.map +1 -0
- package/dist/policies/audit-trail.js +31 -0
- package/dist/policies/audit-trail.js.map +1 -0
- package/dist/secret-state.d.ts +54 -0
- package/dist/secret-state.d.ts.map +1 -0
- package/dist/secret-state.js +2 -0
- package/dist/secret-state.js.map +1 -0
- package/dist/sinks/index.d.ts +9 -0
- package/dist/sinks/index.d.ts.map +1 -0
- package/dist/sinks/index.js +5 -0
- package/dist/sinks/index.js.map +1 -0
- package/dist/sinks/jsonl.d.ts +16 -0
- package/dist/sinks/jsonl.d.ts.map +1 -0
- package/dist/sinks/jsonl.js +51 -0
- package/dist/sinks/jsonl.js.map +1 -0
- package/dist/sinks/otel.d.ts +22 -0
- package/dist/sinks/otel.d.ts.map +1 -0
- package/dist/sinks/otel.js +155 -0
- package/dist/sinks/otel.js.map +1 -0
- package/dist/sinks/queue.d.ts +12 -0
- package/dist/sinks/queue.d.ts.map +1 -0
- package/dist/sinks/queue.js +89 -0
- package/dist/sinks/queue.js.map +1 -0
- package/dist/sinks/syslog.d.ts +17 -0
- package/dist/sinks/syslog.d.ts.map +1 -0
- package/dist/sinks/syslog.js +103 -0
- package/dist/sinks/syslog.js.map +1 -0
- package/dist/sinks/types.d.ts +27 -0
- package/dist/sinks/types.d.ts.map +1 -0
- package/dist/sinks/types.js +18 -0
- package/dist/sinks/types.js.map +1 -0
- package/dist/vault/policy.d.ts +64 -34
- package/dist/vault/policy.d.ts.map +1 -1
- package/dist/vault/policy.js +109 -67
- package/dist/vault/policy.js.map +1 -1
- package/dist/vault/session-vault-errors.d.ts +72 -4
- package/dist/vault/session-vault-errors.d.ts.map +1 -1
- package/dist/vault/session-vault-errors.js +95 -4
- package/dist/vault/session-vault-errors.js.map +1 -1
- package/dist/vault/session-vault.d.ts +145 -2
- package/dist/vault/session-vault.d.ts.map +1 -1
- package/dist/vault/session-vault.js +548 -72
- package/dist/vault/session-vault.js.map +1 -1
- package/dist/vault/sidecar.d.ts +9 -0
- package/dist/vault/sidecar.d.ts.map +1 -1
- package/dist/vault/sidecar.js +72 -3
- package/dist/vault/sidecar.js.map +1 -1
- package/dist/vault/types.d.ts +58 -10
- package/dist/vault/types.d.ts.map +1 -1
- package/dist/vault/types.js.map +1 -1
- package/dist/vault/vault-env.d.ts +43 -50
- package/dist/vault/vault-env.d.ts.map +1 -1
- package/dist/vault/vault-env.js +141 -62
- package/dist/vault/vault-env.js.map +1 -1
- package/dist/vault/vault-gpg.d.ts +6 -1
- package/dist/vault/vault-gpg.d.ts.map +1 -1
- package/dist/vault/vault-gpg.js +10 -8
- package/dist/vault/vault-gpg.js.map +1 -1
- package/dist/vault/vault-libsecret.d.ts +6 -1
- package/dist/vault/vault-libsecret.d.ts.map +1 -1
- package/dist/vault/vault-libsecret.js +16 -14
- package/dist/vault/vault-libsecret.js.map +1 -1
- package/dist/vault/vault-onepassword.d.ts +119 -0
- package/dist/vault/vault-onepassword.d.ts.map +1 -0
- package/dist/vault/vault-onepassword.js +271 -0
- package/dist/vault/vault-onepassword.js.map +1 -0
- package/dist/vault/vault-utils.d.ts +37 -3
- package/dist/vault/vault-utils.d.ts.map +1 -1
- package/dist/vault/vault-utils.js +65 -3
- package/dist/vault/vault-utils.js.map +1 -1
- package/dist/vault/vault-windows.d.ts +7 -2
- package/dist/vault/vault-windows.d.ts.map +1 -1
- package/dist/vault/vault-windows.js +21 -16
- package/dist/vault/vault-windows.js.map +1 -1
- package/dist/vault/vault.d.ts +120 -9
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +222 -139
- package/dist/vault/vault.js.map +1 -1
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -2,8 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
Cross-platform secrets vault with AES-256-GCM encryption and platform-native key storage.
|
|
4
4
|
|
|
5
|
+
> **Before you deploy this:** read the [threat model](../../docs/threat-model.md). It states what the library defends against, what it explicitly does **not** defend against (including two gaps that are live on the default path — the shared `centient-vault` Keychain master-key item, and the master key transiting argv on the macOS Keychain key write), where the trust boundaries are, and a deployment checklist.
|
|
6
|
+
|
|
5
7
|
> **Daemons / long-running processes:** see [Session-backed vault (`openVault`)](./docs/session-vault.md) for the recommended API — single master-key unlock per session, in-memory cached reads, mtime-check coherence with the CLI, rollback protection via monotonic version + sidecar.
|
|
6
8
|
|
|
9
|
+
> **Creating a session vault:** use `createVault({ path, key })`. It composes the
|
|
10
|
+
> initial AAD-bound ciphertext and rollback sidecar without exposing their
|
|
11
|
+
> private wire format. It is non-destructive, never manages provider state, and
|
|
12
|
+
> refuses incomplete vault/sidecar pairs. See the [first-use guide](./docs/session-vault.md#first-use-creation).
|
|
13
|
+
|
|
7
14
|
## Installation
|
|
8
15
|
|
|
9
16
|
```bash
|
|
@@ -21,23 +28,188 @@ pnpm add @centient/secrets
|
|
|
21
28
|
- AES-256-GCM authenticated encryption for secrets at rest
|
|
22
29
|
- Platform-native key storage (macOS Keychain, Linux secret-service)
|
|
23
30
|
- Pluggable key providers (Keychain, 1Password, passphrase)
|
|
24
|
-
- Credential vault with session management
|
|
31
|
+
- Credential vault with session management, plus an opt-in 1Password credential backend
|
|
32
|
+
- Instance-scoped `SecretsClient` factories with composable policy middleware
|
|
25
33
|
- Environment detection (CI, Docker, SSH, headless, agent)
|
|
26
34
|
- Built-in CLI for interactive secret management
|
|
27
35
|
|
|
28
36
|
## Quick Start
|
|
29
37
|
|
|
38
|
+
The client factory is the primary API when a consumer needs an explicit storage
|
|
39
|
+
provider, caller identity, or policies. Each client snapshots its policy-stack
|
|
40
|
+
membership, so two consumers in one process cannot accidentally add or remove
|
|
41
|
+
one another's enforcement. Policy instances may retain their own internal state.
|
|
42
|
+
|
|
30
43
|
```typescript
|
|
31
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
KeychainVault,
|
|
46
|
+
auditTrail,
|
|
47
|
+
createSecretsClient,
|
|
48
|
+
type CredentialAuditEvent,
|
|
49
|
+
} from "@centient/secrets";
|
|
50
|
+
|
|
51
|
+
const auditBuffer: CredentialAuditEvent[] = [];
|
|
52
|
+
const client = createSecretsClient({
|
|
53
|
+
provider: new KeychainVault(),
|
|
54
|
+
policies: [
|
|
55
|
+
auditTrail({
|
|
56
|
+
sink: (event) => auditBuffer.push(event),
|
|
57
|
+
includeReads: true,
|
|
58
|
+
}),
|
|
59
|
+
],
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
await client.storeCredential("my-service.api-key", "<your-api-key>", {
|
|
63
|
+
caller: { id: "my-service", kind: "service" },
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const value = await client.getCredential("my-service.api-key", {
|
|
67
|
+
caller: { id: "my-service", kind: "service" },
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await client.deleteCredential("my-service.api-key", {
|
|
71
|
+
caller: { id: "my-service", kind: "service" },
|
|
72
|
+
});
|
|
73
|
+
```
|
|
32
74
|
|
|
33
|
-
|
|
34
|
-
|
|
75
|
+
`SecretsProvider` is the credential-*value* storage SPI used by the factory.
|
|
76
|
+
The older `VaultBackend` name remains as a deprecated type alias, so existing
|
|
77
|
+
implementations continue to compile. This is separate from `KeyProvider`, which
|
|
78
|
+
locates the master key used to open an encrypted session vault.
|
|
35
79
|
|
|
36
|
-
|
|
37
|
-
|
|
80
|
+
Policies use onion ordering: `before` runs top-to-bottom and `after` runs
|
|
81
|
+
bottom-to-top. A throwing `before` fails closed without contacting the provider;
|
|
82
|
+
the rejecting policy and every previously entered policy still receive the
|
|
83
|
+
rejection event while later policies do not run. Operation metadata includes the
|
|
84
|
+
operation, key or prefix, and optional caller identity, but never the credential
|
|
85
|
+
value. Because `auditTrail()` promises to observe policy denials, place it before
|
|
86
|
+
every policy that has a `before` hook. Client construction and legacy policy
|
|
87
|
+
replacement reject an unsafe order instead of silently leaving denials unaudited.
|
|
88
|
+
|
|
89
|
+
### Audit telemetry: OpenTelemetry + OCSF
|
|
90
|
+
|
|
91
|
+
Installing an `auditTrail()` policy maps every value-free credential outcome —
|
|
92
|
+
including provider failures and policy denials — to an OCSF 1.9.0 Entity
|
|
93
|
+
Management record. `auditTrail()` defaults to an OpenTelemetry sink; audit is
|
|
94
|
+
not globally enabled merely by importing the package.
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { auditTrail, createSecretsClient } from "@centient/secrets";
|
|
38
98
|
|
|
39
|
-
|
|
40
|
-
|
|
99
|
+
const client = createSecretsClient({
|
|
100
|
+
provider,
|
|
101
|
+
policies: [auditTrail()],
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`@centient/secrets` depends on `@opentelemetry/api`, not an SDK or exporter.
|
|
106
|
+
The application owns tracer-provider, sampler, exporter, collector, and shutdown
|
|
107
|
+
configuration. The sink reports a non-recording or unsampled span as degraded;
|
|
108
|
+
for an audit pipeline, configure an always-on audit sampler and force-flush the
|
|
109
|
+
application's SDK before shutdown.
|
|
110
|
+
|
|
111
|
+
The released OCSF schema corrects an older ADR-002 label: `3` is the Identity &
|
|
112
|
+
Access Management category and `3004` is the **Entity Management class**; there
|
|
113
|
+
is no released class named Credential Activity. Stores are upserts and
|
|
114
|
+
enumeration has no 3004 activity, so both use `activity_id: 99` (`Store` or
|
|
115
|
+
`Enumerate`) instead of claiming a create/update distinction the provider SPI
|
|
116
|
+
cannot observe. Policy denial is `action_id: 2` with `status_id: 2`.
|
|
117
|
+
|
|
118
|
+
Sink migration: the pre-1.0 audit seam passed its internal `SecretsEvent`
|
|
119
|
+
directly to a callback. A sink now receives `CredentialAuditEvent`. Read the
|
|
120
|
+
credential key from `entity.name`, provider from `entity.data.vault_name`, raw
|
|
121
|
+
event classification from `unmapped["centient.secrets.event_type"]`, and latency
|
|
122
|
+
from `unmapped["centient.secrets.operation.duration_ms"]`. Credential values and
|
|
123
|
+
provider/policy exception text are not copied into the OCSF record.
|
|
124
|
+
|
|
125
|
+
Non-OTel deployments can use the JSONL or RFC 5424 syslog sinks. Both enqueue in
|
|
126
|
+
emission order, bound their queues, report background failures, and expose
|
|
127
|
+
`flush()`/`close()` so a caller can observe delivery failure:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { auditTrail, createSecretsClient } from "@centient/secrets";
|
|
131
|
+
import { jsonlAuditSink } from "@centient/secrets/sinks";
|
|
132
|
+
|
|
133
|
+
const sink = jsonlAuditSink({ filePath: "/var/log/my-service/secrets.jsonl" });
|
|
134
|
+
const client = createSecretsClient({ provider, policies: [auditTrail({ sink })] });
|
|
135
|
+
|
|
136
|
+
// ...credential operations...
|
|
137
|
+
await sink.flush?.();
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
JSONL contains bare OCSF records. Syslog serializes the same record as the RFC
|
|
141
|
+
5424 message body. New JSONL files/directories default to owner-only modes
|
|
142
|
+
(`0o600`/`0o700`); existing permissions are not silently changed. UDP syslog's
|
|
143
|
+
`flush()` confirms local socket acceptance, not remote persistence, and UDP does
|
|
144
|
+
not provide transport encryption or authentication—use a local syslog daemon or
|
|
145
|
+
inject a protected sender for a remote hop. Neither sink routes through
|
|
146
|
+
`@centient/logger`'s
|
|
147
|
+
`AuditWriter` or the SDK's remote `AuditResource`: both existing seams have a
|
|
148
|
+
different closed event schema and async/failure contract. Keeping the
|
|
149
|
+
`AuditSink` boundary structural prevents either package from silently reshaping
|
|
150
|
+
or truncating a signed record.
|
|
151
|
+
|
|
152
|
+
#### HMAC record-integrity chains
|
|
153
|
+
|
|
154
|
+
Create an optional process-local chain with a dedicated 32-byte audit key. The
|
|
155
|
+
consumer must load that key from its deployment trust store and inject it; the
|
|
156
|
+
library never reads a file/environment/provider implicitly, silently generates
|
|
157
|
+
a key, or reuses/derives from the vault-encryption key managed by `KeyProvider`.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import {
|
|
161
|
+
auditTrail,
|
|
162
|
+
createHmacAuditChain,
|
|
163
|
+
createSecretsClient,
|
|
164
|
+
verifyHmacAuditChain,
|
|
165
|
+
} from "@centient/secrets";
|
|
166
|
+
import { jsonlAuditSink } from "@centient/secrets/sinks";
|
|
167
|
+
|
|
168
|
+
const chain = createHmacAuditChain({
|
|
169
|
+
chainId: "my-service-secrets",
|
|
170
|
+
keyId: "audit-key-2026-08",
|
|
171
|
+
key: auditKeyBytes, // exactly 32 bytes, loaded by the application
|
|
172
|
+
});
|
|
173
|
+
const sink = jsonlAuditSink({ filePath: "/var/log/my-service/secrets.jsonl" });
|
|
174
|
+
const client = createSecretsClient({
|
|
175
|
+
provider,
|
|
176
|
+
policies: [auditTrail({ sink, chain })],
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Rotate only at an explicit emission boundary; IDs and key material may never be reused.
|
|
180
|
+
chain.rotate({ keyId: "audit-key-2026-09", key: nextAuditKeyBytes });
|
|
181
|
+
|
|
182
|
+
await sink.flush?.();
|
|
183
|
+
const anchor = chain.checkpoint();
|
|
184
|
+
// Persist `anchor` outside the audit log's trust domain.
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Each chained event uses OCSF 1.9's Record Integrity profile:
|
|
188
|
+
`metadata.sequence`, a chain UID, the current HMAC fingerprint, and the previous
|
|
189
|
+
event reference. `verifyHmacAuditChain()` accepts a trusted key resolver; bind
|
|
190
|
+
verification to the deployment's trusted `expectedChainId` so another valid
|
|
191
|
+
chain cannot be substituted. The verifier
|
|
192
|
+
returns a discriminated result rather than throwing on hostile records. It
|
|
193
|
+
detects content mutation (including the final event), insertion, duplication,
|
|
194
|
+
internal deletion, reorder, unknown/wrong keys, and invalid rotations. Supply a
|
|
195
|
+
trusted initial checkpoint when verifying a resumed segment and an expected
|
|
196
|
+
final checkpoint to detect prefix/tail deletion; HMAC alone cannot prove that
|
|
197
|
+
an attacker did not delete the whole log. Checkpoints carry chain-scoped,
|
|
198
|
+
HMAC-derived key fingerprints (never key bytes), so historical key material
|
|
199
|
+
cannot be reintroduced under a fresh ID after restart. A global multi-process sequence needs
|
|
200
|
+
an external serialized coordinator—each independent process should otherwise
|
|
201
|
+
use its own `chainId`.
|
|
202
|
+
|
|
203
|
+
The module-level functions remain the simple, backwards-compatible path. They
|
|
204
|
+
delegate through the process's default client and the legacy global policy
|
|
205
|
+
configuration:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import { storeCredential, getCredential, deleteCredential } from "@centient/secrets";
|
|
209
|
+
|
|
210
|
+
await storeCredential("my-service.api-key", "<your-api-key>");
|
|
211
|
+
const value = await getCredential("my-service.api-key");
|
|
212
|
+
await deleteCredential("my-service.api-key");
|
|
41
213
|
```
|
|
42
214
|
|
|
43
215
|
### Encryption Utilities
|
|
@@ -60,6 +232,34 @@ if (isCIEnvironment()) {
|
|
|
60
232
|
}
|
|
61
233
|
```
|
|
62
234
|
|
|
235
|
+
### Auth CLI messages
|
|
236
|
+
|
|
237
|
+
`AUTH_MESSAGES` is the shared, i18n-ready catalog of user-visible strings for
|
|
238
|
+
the credential lifecycle a CLI drives on top of this vault — `login`, `logout`,
|
|
239
|
+
`auth status`, `auth refresh`, device flow, and api-key entry. Import it; do not
|
|
240
|
+
copy it. It is public precisely so a consuming CLI stops maintaining a fork that
|
|
241
|
+
drifts from this one.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import { AUTH_MESSAGES, type AuthMessages } from "@centient/secrets";
|
|
245
|
+
|
|
246
|
+
process.stderr.write(AUTH_MESSAGES.error.vaultWriteFailed + "\n");
|
|
247
|
+
process.stderr.write(AUTH_MESSAGES.info.loginPrompt(verificationUri) + "\n");
|
|
248
|
+
process.stderr.write(AUTH_MESSAGES.warning.tokenExpiringSoon(5) + "\n");
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
No value interpolates at the call site: fixed messages are string constants and
|
|
252
|
+
parameterized ones are template functions returning the finished string, so a
|
|
253
|
+
translation layer can replace the catalog wholesale. `AuthMessages` is the
|
|
254
|
+
catalog's type, for typing such a table.
|
|
255
|
+
|
|
256
|
+
**Scope.** The catalog covers the auth lifecycle above, plus the one
|
|
257
|
+
credential-storage warning this package itself emits
|
|
258
|
+
(`warning.envVaultNoStorage`, from the read-only `EnvVault` fallback). It does
|
|
259
|
+
**not** cover the `centient secrets …` operator CLI (`runSecrets`) or the
|
|
260
|
+
`[secrets] …` vault diagnostics — those are command-specific and carry their own
|
|
261
|
+
next-step lines, so they stay at their call sites by design.
|
|
262
|
+
|
|
63
263
|
## Key Providers
|
|
64
264
|
|
|
65
265
|
| Provider | Platform | Description |
|
|
@@ -72,6 +272,100 @@ Provider auto-detection prefers OS-backed storage: 1Password, then Keychain,
|
|
|
72
272
|
then passphrase as the last fallback. Set `secrets.provider: "passphrase"` in
|
|
73
273
|
`~/.centient/config.json` to select it explicitly.
|
|
74
274
|
|
|
275
|
+
## Credential storage backends
|
|
276
|
+
|
|
277
|
+
The **key** layer above decides where the vault *encryption key* lives. This is
|
|
278
|
+
the separate **credential** layer: where secret *values* are stored.
|
|
279
|
+
|
|
280
|
+
| Backend | Platform | Selection |
|
|
281
|
+
|---|---|---|
|
|
282
|
+
| `KeychainVault` | macOS | auto |
|
|
283
|
+
| `WindowsVault` | Windows / WSL | auto |
|
|
284
|
+
| `LibsecretVault` | Linux | auto |
|
|
285
|
+
| `GpgVault` | Linux / WSL | auto |
|
|
286
|
+
| `EnvVault` | Any | auto (last resort) |
|
|
287
|
+
| `OnePasswordVault` | Any (needs `op`) | **explicit opt-in only** |
|
|
288
|
+
|
|
289
|
+
The first five form an auto-cascade, picked by `detect()` in that order.
|
|
290
|
+
`OnePasswordVault` is deliberately **outside** it: having the 1Password CLI
|
|
291
|
+
installed is not consent to route credentials into your personal vault, so it is
|
|
292
|
+
reachable only when you ask for it by name (ADR-004).
|
|
293
|
+
|
|
294
|
+
```jsonc
|
|
295
|
+
// centient config file
|
|
296
|
+
{
|
|
297
|
+
"secrets": {
|
|
298
|
+
"provider": "keychain", // KEY layer — key stays in the Keychain
|
|
299
|
+
"backend": "1password", // VALUE layer — credentials in 1Password
|
|
300
|
+
"onePasswordBackend": {
|
|
301
|
+
"vault": "centient-credentials", // REQUIRED — no default
|
|
302
|
+
"tag": "centient" // optional
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Environment equivalents, which take precedence:
|
|
309
|
+
`CENTIENT_SECRETS_BACKEND=1password` and `CENTIENT_OP_VAULT=<name>`.
|
|
310
|
+
|
|
311
|
+
Two behaviours worth knowing:
|
|
312
|
+
|
|
313
|
+
- **No default vault, and it fails closed.** Unlike the key block (which defaults
|
|
314
|
+
to `Private`), an unset `onePasswordBackend.vault` under an explicit
|
|
315
|
+
`backend: "1password"` is an error. Guessing could write credentials into a
|
|
316
|
+
vault you did not intend.
|
|
317
|
+
- **An explicit choice is never silently substituted.** If you name `1password`
|
|
318
|
+
and `op` turns out to be missing or unauthenticated, startup throws rather than
|
|
319
|
+
quietly falling back to the Keychain — otherwise your secrets would land
|
|
320
|
+
somewhere other than where you said.
|
|
321
|
+
|
|
322
|
+
Secret values are written over **stdin** (`op item create -`), never as argv, so
|
|
323
|
+
they never appear in `ps`. Only key *names* are cached (5s TTL, mirroring the
|
|
324
|
+
Keychain backend); values are never cached, so a rotated or revoked credential is
|
|
325
|
+
never served from memory.
|
|
326
|
+
|
|
327
|
+
**Key constraint.** This backend enforces `isValidKey` (lowercase alphanumeric
|
|
328
|
+
with `-` or `.` separators, 2–64 chars) on every operation, and refuses anything
|
|
329
|
+
else rather than storing it. Reads address the value as
|
|
330
|
+
`op://<vault>/<key>/password`, which is path-structured: a key containing `/`
|
|
331
|
+
would store fine — a 1Password item title is just a string — and then re-parse on
|
|
332
|
+
read into a different item and field, so the write would be silently unreadable.
|
|
333
|
+
Refusing is the better failure; a caller believing a credential is saved when it
|
|
334
|
+
cannot be read back is worse than a caller told no.
|
|
335
|
+
|
|
336
|
+
### Environment fallback key mapping
|
|
337
|
+
|
|
338
|
+
`EnvVault` is the read-only terminal backend for hosts with no secure credential
|
|
339
|
+
store. Every valid logical credential key has exactly one environment-variable
|
|
340
|
+
name, so a miss means that variable is genuinely unset rather than that the
|
|
341
|
+
backend does not know how to address the key. The historical mapping is retained:
|
|
342
|
+
|
|
343
|
+
| Credential key | Environment variable |
|
|
344
|
+
|---|---|
|
|
345
|
+
| `auth-token` | `ENGRAM_API_KEY` |
|
|
346
|
+
| `sync-peers.dek` | `CENTIENT_SECRET_SYNC_2DPEERS_2EDEK` |
|
|
347
|
+
|
|
348
|
+
For non-auth keys, alphanumerics are uppercased, `-` is escaped as `_2D`, `.` as
|
|
349
|
+
`_2E`, and the result is prefixed with `CENTIENT_SECRET_`. `_` is forbidden by
|
|
350
|
+
the credential-key grammar, so the mapping stays reversible even for adjacent
|
|
351
|
+
separators. Use the exported helper instead of reimplementing it:
|
|
352
|
+
|
|
353
|
+
```ts
|
|
354
|
+
import { EnvVault, createSecretsClient, credentialKeyToEnvName } from "@centient/secrets";
|
|
355
|
+
|
|
356
|
+
credentialKeyToEnvName("sync-peers.dek");
|
|
357
|
+
// => "CENTIENT_SECRET_SYNC_2DPEERS_2EDEK"
|
|
358
|
+
|
|
359
|
+
const client = createSecretsClient({ provider: new EnvVault() });
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
An unset variable probes as `absent`. A defined-but-empty variable returns
|
|
363
|
+
`null` on retrieval and probes as `failed`, because zero bytes are not a usable
|
|
364
|
+
credential. `store()` remains read-only and returns `false`; `delete()` remains
|
|
365
|
+
an idempotent no-op. Environment values are unencrypted, inherited by child
|
|
366
|
+
processes, and may be readable through `/proc/<pid>/environ`; this fallback is
|
|
367
|
+
configuration plumbing, not secure storage.
|
|
368
|
+
|
|
75
369
|
### Per-consumer vault keys
|
|
76
370
|
|
|
77
371
|
By default `KeychainProvider` targets a single shared Keychain item
|
|
@@ -81,6 +375,11 @@ let each consumer use its own key (issue #80). Both are additive — with no
|
|
|
81
375
|
options the behaviour is byte-identical to before, and existing vaults keep
|
|
82
376
|
opening.
|
|
83
377
|
|
|
378
|
+
**This is opt-in, and the shared item is still the default.** Until you pass one
|
|
379
|
+
of the options below, anything that can read the `centient-vault` Keychain item
|
|
380
|
+
unlocks every consumer's vault on the machine — see
|
|
381
|
+
[threat model §4.1](../../docs/threat-model.md#41-the-shared-centient-vault-master-key-item-is-still-the-default-issue-80).
|
|
382
|
+
|
|
84
383
|
**Name your own Keychain item** — the lightweight path. Pass `keychain` to
|
|
85
384
|
`openVault()` (threaded into internal provider resolution) so your consumer's
|
|
86
385
|
master key lives under its own Keychain item:
|
|
@@ -122,6 +421,13 @@ const vault = await openVault({ keyProvider: stub });
|
|
|
122
421
|
A custom provider can also wrap any backend (remote KMS, HSM, env-injected key)
|
|
123
422
|
as long as it implements the `KeyProvider` interface.
|
|
124
423
|
|
|
424
|
+
`KeyProvider` is intentionally separate from `SecretsProvider`, the
|
|
425
|
+
credential-value storage SPI. A key provider establishes or retrieves the one
|
|
426
|
+
master key used to unlock an encrypted session vault; a secrets provider stores
|
|
427
|
+
the individual credential values. The same technology may fill both roles—for
|
|
428
|
+
example, `OnePasswordProvider` and `OnePasswordVault`—but their instances,
|
|
429
|
+
configuration, key spaces, and operation lifecycles remain independent.
|
|
430
|
+
|
|
125
431
|
### Passphrase provider
|
|
126
432
|
|
|
127
433
|
For hosts without an OS keychain or 1Password CLI (e.g. a headless Linux box
|
|
@@ -153,6 +459,116 @@ Security tradeoffs vs OS-backed providers — choose deliberately:
|
|
|
153
459
|
of ms); daemons should call `openVault()` once at startup, before entering
|
|
154
460
|
their hot loop.
|
|
155
461
|
|
|
462
|
+
## Compatibility floor for consumers
|
|
463
|
+
|
|
464
|
+
Several releases of this package closed defects whose only symptom, at the old
|
|
465
|
+
version, is **silence**: a denied credential operation that leaves no audit
|
|
466
|
+
trace, a keychain write that reports success into a keychain the current
|
|
467
|
+
context's reader cannot see, a malformed key that stores on one backend and
|
|
468
|
+
reads as absent on the next. A consumer running such a version cannot learn
|
|
469
|
+
that from the package — the behaviour looks fine and the logs look clean.
|
|
470
|
+
|
|
471
|
+
So the provider publishes the list. `COMPATIBILITY_FLOOR` is a data table of
|
|
472
|
+
every known behavioural gap, keyed by the version that closed it, and
|
|
473
|
+
`assessCompatibility()` turns an installed version into a grade a `doctor`
|
|
474
|
+
command can print.
|
|
475
|
+
|
|
476
|
+
```ts
|
|
477
|
+
import { assessCompatibility, SECRETS_PACKAGE_VERSION } from "@centient/secrets";
|
|
478
|
+
|
|
479
|
+
const assessment = assessCompatibility(SECRETS_PACKAGE_VERSION);
|
|
480
|
+
|
|
481
|
+
if (assessment.grade !== "ok") {
|
|
482
|
+
console.warn(assessment.summary);
|
|
483
|
+
for (const gap of assessment.openGaps) {
|
|
484
|
+
console.warn(` [${gap.severity}] ${gap.symptom}`);
|
|
485
|
+
console.warn(` fixed in ${gap.closedIn} (${gap.reference})`);
|
|
486
|
+
if (gap.adoptionNote) console.warn(` on adoption: ${gap.adoptionNote}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
`SECRETS_PACKAGE_VERSION` is the version of the build you actually imported, so
|
|
492
|
+
the assessment describes the code in your tree rather than whatever is latest on
|
|
493
|
+
the registry. It is kept in step with `package.json` by the release flow and
|
|
494
|
+
asserted by a test, so it cannot drift into a wrong answer.
|
|
495
|
+
|
|
496
|
+
### The grades
|
|
497
|
+
|
|
498
|
+
| `grade` | Meaning |
|
|
499
|
+
|---|---|
|
|
500
|
+
| `ok` | At or above every known fix. `openGaps` is empty. |
|
|
501
|
+
| `degraded` | Assessable, and one or more known gaps are open. `openGaps` lists them, oldest fix first; `highestSeverity` is the worst. |
|
|
502
|
+
| `unsupported` | Below `MIN_ASSESSABLE_VERSION` — older than the table describes. The gaps it *does* know about are still returned, but the list is not claimed to be complete. |
|
|
503
|
+
| `unknown` | The version could not be parsed. `reason` says why. |
|
|
504
|
+
|
|
505
|
+
### This is a graded floor, never a boot-time refusal
|
|
506
|
+
|
|
507
|
+
Nothing in this surface runs at import, nothing throws from a constructor, and
|
|
508
|
+
no vault path consults it. Every function is pure, total and advisory:
|
|
509
|
+
malformed input comes back as a typed `unknown` grade carrying the reason, not
|
|
510
|
+
as an exception. A package that refuses to load because its caller is old turns
|
|
511
|
+
a documentation problem into an outage — so grade, report, and let the consumer
|
|
512
|
+
decide.
|
|
513
|
+
|
|
514
|
+
### Auditing a manifest pin instead of an install
|
|
515
|
+
|
|
516
|
+
`assessCompatibility()` answers "what is open in the build I imported".
|
|
517
|
+
`assessPin()` answers "what could be open in any build this pin admits", by
|
|
518
|
+
grading the lowest version the range allows — which is also what a lockfile
|
|
519
|
+
that has never been refreshed is most likely holding:
|
|
520
|
+
|
|
521
|
+
```ts
|
|
522
|
+
assessPin("^0.6.0").openGaps.map((g) => g.id);
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
#### The range grammar `assessPin()` accepts
|
|
526
|
+
|
|
527
|
+
The whole range is matched against an anchored grammar, so an unsupported form
|
|
528
|
+
is rejected *as* unsupported rather than partially interpreted. Exactly four
|
|
529
|
+
forms resolve:
|
|
530
|
+
|
|
531
|
+
| Form | Example | Resolves to |
|
|
532
|
+
|---|---|---|
|
|
533
|
+
| caret | `^0.6.0` | `0.6.0` |
|
|
534
|
+
| tilde | `~0.9.1` | `0.9.1` |
|
|
535
|
+
| inclusive lower bound | `>=0.8.0` | `0.8.0` |
|
|
536
|
+
| exact | `0.10.0` | `0.10.0` |
|
|
537
|
+
|
|
538
|
+
The version component is a full `major.minor.patch`. A conventional leading `v`
|
|
539
|
+
(`v0.5.0`), horizontal whitespace after the operator (`>= 0.8.0`) and around the
|
|
540
|
+
whole range, and a `-prerelease` suffix are all accepted; the pre-release is
|
|
541
|
+
**kept**, because rounding `^1.0.0-rc.1` up to `1.0.0` would credit a consumer
|
|
542
|
+
for a release it is not running.
|
|
543
|
+
|
|
544
|
+
Everything else returns `grade: "unknown"` with a `reason` naming the accepted
|
|
545
|
+
grammar: composite ranges (`>=0.6.0 <0.8.0`), `||` unions, hyphen ranges
|
|
546
|
+
(`1.0.0 - 2.0.0`), wildcards (`*`), x-ranges (`1.x`, `1.2.x`), partial versions
|
|
547
|
+
(`1.2`), upper and exclusive bounds (`<0.9.0`, `>0.9.0`), protocol pins
|
|
548
|
+
(`workspace:*`, `npm:…`, `file:…`, a git URL), build metadata (`^1.2.3+build`,
|
|
549
|
+
which a parser *discards* rather than interprets), and any form carrying a
|
|
550
|
+
trailing token. A guess would grade the wrong version and hand back a clean bill
|
|
551
|
+
of health for a pin nobody actually checked.
|
|
552
|
+
|
|
553
|
+
#### Malformed input is graded, never thrown
|
|
554
|
+
|
|
555
|
+
`assessCompatibility()` and `assessPin()` narrow their argument through the same
|
|
556
|
+
guard before anything parses it, so a JS caller — or a `JSON.parse`d manifest,
|
|
557
|
+
or an `any` at a module boundary — that passes `null`, `undefined`, a number or
|
|
558
|
+
an object gets the documented `grade: "unknown"` result carrying a `reason`,
|
|
559
|
+
never an exception. `installedVersion` and `summary` are strings in that case
|
|
560
|
+
too, so the echo cannot hand back a value its declared type forbids.
|
|
561
|
+
|
|
562
|
+
### Adding an entry
|
|
563
|
+
|
|
564
|
+
A release earns a row when it closes a defect whose pre-fix symptom is silent.
|
|
565
|
+
A fix a consumer would notice on its own does not need one. Each entry carries
|
|
566
|
+
an `id` (stable — consumers may suppress by it), the `closedIn` version, a
|
|
567
|
+
`severity`, a one-line consumer-facing `symptom`, the `reference` issue, and an
|
|
568
|
+
`adoptionNote` whenever adopting the fix is not a pure no-op. The table is
|
|
569
|
+
ordered oldest fix first; `tests/compatibility.test.ts` pins the ordering, the
|
|
570
|
+
version parity, and the grade each known consumer pin earns.
|
|
571
|
+
|
|
156
572
|
## License
|
|
157
573
|
|
|
158
574
|
MIT
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { CredentialAuditEvent, CredentialAuditMetadata, CredentialAuditTypeUid, CredentialAuditUnmapped, OcsfAttestation } from "./ocsf.js";
|
|
2
|
+
export type AuditChainErrorCode = "INVALID_CHAIN_ID" | "INVALID_KEY_ID" | "INVALID_KEY" | "INVALID_CHECKPOINT" | "CHAIN_ID_MISMATCH" | "CHECKPOINT_KEY_MISMATCH" | "KEY_ID_REUSE" | "KEY_MATERIAL_REUSE" | "EVENT_ALREADY_CHAINED" | "SEQUENCE_EXHAUSTED" | "CHAIN_DESTROYED" | "CANONICALIZATION_FAILED";
|
|
3
|
+
/** Configuration/state errors produced by the writer side of the chain. */
|
|
4
|
+
export declare class AuditChainError extends Error {
|
|
5
|
+
readonly code: AuditChainErrorCode;
|
|
6
|
+
constructor(code: AuditChainErrorCode, message: string);
|
|
7
|
+
}
|
|
8
|
+
export interface AuditChainKey {
|
|
9
|
+
/** Stable, non-secret key version identifier. Never reuse an ID. */
|
|
10
|
+
readonly keyId: string;
|
|
11
|
+
/** Exactly 32 bytes. The chain copies the bytes and never owns this input. */
|
|
12
|
+
readonly key: Uint8Array;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Value-free state needed to resume or externally anchor a chain.
|
|
16
|
+
*
|
|
17
|
+
* A checkpoint is trustworthy only when the caller stores it outside the
|
|
18
|
+
* audit log's own trust domain. Supplying a checkpoint read from the same
|
|
19
|
+
* mutable log cannot prove that a prefix or suffix was not deleted.
|
|
20
|
+
*/
|
|
21
|
+
export interface AuditChainCheckpoint {
|
|
22
|
+
readonly chainId: string;
|
|
23
|
+
readonly sequenceNumber: number;
|
|
24
|
+
readonly eventUid: string;
|
|
25
|
+
readonly eventHash: string;
|
|
26
|
+
readonly typeUid: CredentialAuditTypeUid;
|
|
27
|
+
readonly keyId: string;
|
|
28
|
+
/** Ordered key IDs already used by this chain, including `keyId`. */
|
|
29
|
+
readonly usedKeyIds: readonly string[];
|
|
30
|
+
/**
|
|
31
|
+
* Chain-scoped, HMAC-derived key identifiers aligned with `usedKeyIds`.
|
|
32
|
+
* These are not key bytes; they let a trusted checkpoint prevent historical
|
|
33
|
+
* key material from being reintroduced under a fresh ID after restart.
|
|
34
|
+
*/
|
|
35
|
+
readonly usedKeyFingerprints: readonly string[];
|
|
36
|
+
}
|
|
37
|
+
export type ChainedCredentialAuditEvent = CredentialAuditEvent & {
|
|
38
|
+
readonly metadata: CredentialAuditMetadata & {
|
|
39
|
+
readonly sequence: number;
|
|
40
|
+
readonly profiles: readonly ["record_integrity"];
|
|
41
|
+
};
|
|
42
|
+
readonly unmapped: CredentialAuditUnmapped & {
|
|
43
|
+
readonly "centient.audit_chain.key_id": string;
|
|
44
|
+
};
|
|
45
|
+
readonly attestation_list: readonly [OcsfAttestation];
|
|
46
|
+
};
|
|
47
|
+
export interface CreateHmacAuditChainOptions extends AuditChainKey {
|
|
48
|
+
readonly chainId: string;
|
|
49
|
+
/** Trusted external state from the last durable event, if resuming. */
|
|
50
|
+
readonly checkpoint?: AuditChainCheckpoint;
|
|
51
|
+
}
|
|
52
|
+
export interface HmacAuditChain {
|
|
53
|
+
/** Append and sign one previously unchained OCSF event synchronously. */
|
|
54
|
+
append(event: CredentialAuditEvent): ChainedCredentialAuditEvent;
|
|
55
|
+
/**
|
|
56
|
+
* Switch the signing key at an emission boundary.
|
|
57
|
+
*
|
|
58
|
+
* The first later event uses the new key ID while linking to the last event
|
|
59
|
+
* produced with the old key. IDs and key material can never be reused within
|
|
60
|
+
* one chain, including after resuming from a trusted checkpoint.
|
|
61
|
+
*/
|
|
62
|
+
rotate(next: AuditChainKey): void;
|
|
63
|
+
/** Latest value-free state, or `null` before the first event. */
|
|
64
|
+
checkpoint(): AuditChainCheckpoint | null;
|
|
65
|
+
/** Zero the chain's private key copy and permanently close the writer. */
|
|
66
|
+
destroy(): void;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* RFC 8785 JSON Canonicalization Scheme for the JSON data model used by OCSF.
|
|
70
|
+
*
|
|
71
|
+
* Objects are plain data objects, names are ordered by UTF-16 code units, and
|
|
72
|
+
* strings/numbers use ECMAScript JSON serialization. Values outside I-JSON
|
|
73
|
+
* (cycles, sparse arrays, accessors, non-finite numbers, lone surrogates,
|
|
74
|
+
* `undefined`, `bigint`, functions, symbols) fail closed.
|
|
75
|
+
*/
|
|
76
|
+
export declare function canonicalizeAuditEvent(value: unknown): string;
|
|
77
|
+
/** Create one synchronous, process-local HMAC-SHA-256 audit chain. */
|
|
78
|
+
export declare function createHmacAuditChain(options: CreateHmacAuditChainOptions): HmacAuditChain;
|
|
79
|
+
export type AuditChainVerificationFailureCode = "MALFORMED_EVENT" | "UNSUPPORTED_ALGORITHM" | "UNSUPPORTED_CANONICALIZATION" | "UNANCHORED_SEGMENT" | "CHAIN_ID_MISMATCH" | "INVALID_SEQUENCE" | "SEQUENCE_GAP_OR_REORDER" | "PREVIOUS_EVENT_MISMATCH" | "PREVIOUS_HASH_MISMATCH" | "UNKNOWN_KEY_ID" | "KEY_RESOLUTION_FAILED" | "INVALID_KEY" | "KEY_ID_REUSE" | "KEY_ID_MATERIAL_MISMATCH" | "KEY_MATERIAL_REUSE" | "HASH_MISMATCH" | "FINAL_CHECKPOINT_MISMATCH";
|
|
80
|
+
export interface AuditChainVerificationFailure {
|
|
81
|
+
readonly code: AuditChainVerificationFailureCode;
|
|
82
|
+
readonly message: string;
|
|
83
|
+
readonly index: number;
|
|
84
|
+
readonly sequenceNumber?: number;
|
|
85
|
+
}
|
|
86
|
+
export type AuditChainVerificationResult = {
|
|
87
|
+
readonly ok: true;
|
|
88
|
+
readonly value: {
|
|
89
|
+
readonly verifiedCount: number;
|
|
90
|
+
readonly rotations: number;
|
|
91
|
+
readonly checkpoint: AuditChainCheckpoint | null;
|
|
92
|
+
};
|
|
93
|
+
} | {
|
|
94
|
+
readonly ok: false;
|
|
95
|
+
readonly error: AuditChainVerificationFailure;
|
|
96
|
+
};
|
|
97
|
+
export interface VerifyAuditChainOptions {
|
|
98
|
+
/** Trusted key lookup. Returned key bytes remain owned by the resolver. */
|
|
99
|
+
readonly resolveKey: (keyId: string) => Uint8Array | undefined;
|
|
100
|
+
/** Expected chain identity, required to reject whole-chain substitution. */
|
|
101
|
+
readonly expectedChainId?: string;
|
|
102
|
+
/** Required when verification starts after the genesis event. */
|
|
103
|
+
readonly initialCheckpoint?: AuditChainCheckpoint;
|
|
104
|
+
/** External terminal anchor used to detect deletion of a chain suffix. */
|
|
105
|
+
readonly expectedFinalCheckpoint?: AuditChainCheckpoint;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Verify one ordered chain segment without mutating records or key material.
|
|
109
|
+
*
|
|
110
|
+
* The function returns a discriminated failure for hostile input. It requires
|
|
111
|
+
* genesis (`sequence=1`, no `prev_event`) unless a trusted initial checkpoint
|
|
112
|
+
* anchors a later segment. An optional trusted final checkpoint detects suffix
|
|
113
|
+
* deletion; no hash-chain algorithm can detect deletion of the entire log by
|
|
114
|
+
* looking only at that log.
|
|
115
|
+
*/
|
|
116
|
+
export declare function verifyHmacAuditChain(events: readonly unknown[], options: VerifyAuditChainOptions): AuditChainVerificationResult;
|
|
117
|
+
//# sourceMappingURL=hmac-chain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hmac-chain.d.ts","sourceRoot":"","sources":["../../src/audit/hmac-chain.ts"],"names":[],"mappings":"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"}
|