@absolutejs/secrets 0.0.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.
- package/LICENSE +94 -0
- package/README.md +115 -0
- package/dist/index.d.ts +148 -0
- package/dist/index.js +334 -0
- package/dist/index.js.map +10 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Business Source License 1.1
|
|
2
|
+
|
|
3
|
+
**Licensor:** Alex Kahn
|
|
4
|
+
|
|
5
|
+
**Licensed Work:** @absolutejs/secrets (https://github.com/absolutejs/secrets)
|
|
6
|
+
|
|
7
|
+
**Change Date:** May 29, 2030
|
|
8
|
+
|
|
9
|
+
**Change License:** Apache License, Version 2.0
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Terms
|
|
14
|
+
|
|
15
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
16
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
17
|
+
Licensor may make an Additional Use Grant, permitting limited production use.
|
|
18
|
+
|
|
19
|
+
### Additional Use Grant
|
|
20
|
+
|
|
21
|
+
You may use the Licensed Work in production, provided your use does not include
|
|
22
|
+
any of the following:
|
|
23
|
+
|
|
24
|
+
1. **Offering a Competing Service.** You may not offer the Licensed Work, or
|
|
25
|
+
any derivative or substantial portion of it, to third parties as a hosted or
|
|
26
|
+
managed service that competes with a hosted secrets-management,
|
|
27
|
+
credential-broker, or runtime-secrets-injection platform (including, but not
|
|
28
|
+
limited to, services like AWS Secrets Manager, HashiCorp Vault Cloud,
|
|
29
|
+
Doppler, 1Password Secrets, Infisical, GCP Secret Manager, Azure Key Vault,
|
|
30
|
+
Akeyless, Cloudflare Workers Secrets, Vercel Environment Variables as a
|
|
31
|
+
product, or any similar hosted offering whose primary value to its users is
|
|
32
|
+
secrets storage, secrets injection at request time, secrets rotation, or
|
|
33
|
+
secrets redaction). This includes any product whose primary value to its
|
|
34
|
+
users is the functionality the Licensed Work provides.
|
|
35
|
+
|
|
36
|
+
2. **Resale or Redistribution as a Standalone Product.** You may not sell,
|
|
37
|
+
license, or distribute the Licensed Work, or any derivative or fork of it,
|
|
38
|
+
as a standalone commercial product.
|
|
39
|
+
|
|
40
|
+
3. **Removal of Attribution.** Any derivative work, fork, or redistribution of
|
|
41
|
+
the Licensed Work must prominently credit AbsoluteJS and include a link to
|
|
42
|
+
the original project repository (https://github.com/absolutejs/secrets).
|
|
43
|
+
|
|
44
|
+
For clarity, the following uses are expressly permitted:
|
|
45
|
+
|
|
46
|
+
- Using the Licensed Work to build and operate your own applications, websites,
|
|
47
|
+
internal tools, or SaaS products (whether commercial or non-commercial), so
|
|
48
|
+
long as the Licensed Work itself is not the primary product you are selling.
|
|
49
|
+
- Using the Licensed Work as a dependency in commercial software you build and
|
|
50
|
+
sell, as long as the software is not itself a competing managed service of
|
|
51
|
+
the kind described in clause 1.
|
|
52
|
+
- Providing consulting, development, or professional services to clients using
|
|
53
|
+
the Licensed Work.
|
|
54
|
+
- Forking and modifying the Licensed Work for your own internal use, provided
|
|
55
|
+
attribution is maintained.
|
|
56
|
+
|
|
57
|
+
### Change Date and Change License
|
|
58
|
+
|
|
59
|
+
On the Change Date specified above, or on such other date as the Licensor may
|
|
60
|
+
specify by written notice, the Licensed Work will be made available under the
|
|
61
|
+
Change License (Apache License, Version 2.0). Until the Change Date, the terms
|
|
62
|
+
of this Business Source License 1.1 apply.
|
|
63
|
+
|
|
64
|
+
### Trademark
|
|
65
|
+
|
|
66
|
+
This license does not grant you any rights to use the "AbsoluteJS" or
|
|
67
|
+
"@absolutejs" name, logo, or any related trademarks. Forks and derivative works
|
|
68
|
+
must not be named or branded in a manner that suggests endorsement by or
|
|
69
|
+
affiliation with AbsoluteJS or the Licensor.
|
|
70
|
+
|
|
71
|
+
### Notices
|
|
72
|
+
|
|
73
|
+
You must not remove or obscure any licensing, copyright, or other notices
|
|
74
|
+
included in the Licensed Work.
|
|
75
|
+
|
|
76
|
+
### No Warranty
|
|
77
|
+
|
|
78
|
+
THE LICENSED WORK IS PROVIDED "AS IS". THE LICENSOR HEREBY DISCLAIMS ALL
|
|
79
|
+
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
|
|
80
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO
|
|
81
|
+
EVENT SHALL THE LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY,
|
|
82
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR
|
|
83
|
+
IN CONNECTION WITH THE LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE
|
|
84
|
+
LICENSED WORK.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Contact
|
|
89
|
+
|
|
90
|
+
For commercial licensing inquiries or additional permissions, contact:
|
|
91
|
+
|
|
92
|
+
- **Alex Kahn**
|
|
93
|
+
- alexkahndev@gmail.com
|
|
94
|
+
- alexkahndev.github.io
|
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# @absolutejs/secrets
|
|
2
|
+
|
|
3
|
+
Host-side secret broker for multi-tenant Bun runtimes. Three jobs, kept narrow:
|
|
4
|
+
|
|
5
|
+
1. **Resolve** secrets through a pluggable adapter. The broker caches the
|
|
6
|
+
value, returns a `{ value, fingerprint }` pair where the fingerprint is
|
|
7
|
+
a sha256 prefix safe to log, and fires an audit event per call.
|
|
8
|
+
2. **Redact** known cached secrets out of arbitrary strings (an error
|
|
9
|
+
message, a stdout line, a stack trace) before the text lands in any
|
|
10
|
+
log sink. Replacement: `[REDACTED:NAME]`.
|
|
11
|
+
3. **Rotate** through the adapter, invalidate the cache, return the new
|
|
12
|
+
value.
|
|
13
|
+
|
|
14
|
+
Pure logic, zero Bun / Elysia surface. The intended consumers inside the
|
|
15
|
+
SB-6 substrate are `@absolutejs/sync`'s `bridgeFetch.authorization()` hook
|
|
16
|
+
(host-side credential injection for sandboxed mutations) and `unsafeHost`
|
|
17
|
+
declarations (per-customer host functions like Stripe charge, Slack ping,
|
|
18
|
+
queue push).
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { createSecretBroker, envAdapter, inMemoryAdapter, compositeAdapter } from '@absolutejs/secrets';
|
|
22
|
+
|
|
23
|
+
const broker = createSecretBroker({
|
|
24
|
+
adapter: compositeAdapter([
|
|
25
|
+
inMemoryAdapter({ initial: { TEST_KEY: 'sk_test_local_value' } }),
|
|
26
|
+
envAdapter({ prefix: 'ABS_SECRET_' }),
|
|
27
|
+
]),
|
|
28
|
+
audit: (event) => observabilitySink.write(event),
|
|
29
|
+
cacheTtlMs: 60_000,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// In bridgeFetch.authorization():
|
|
33
|
+
const { value, fingerprint } = (await broker.resolve('STRIPE_KEY'))!;
|
|
34
|
+
logger.info('charging', { tenant, fingerprint }); // safe — no plaintext
|
|
35
|
+
return { 'Authorization': `Bearer ${value}` };
|
|
36
|
+
|
|
37
|
+
// In a log sink, before text leaves the host:
|
|
38
|
+
const sanitized = broker.redact(line); // [REDACTED:STRIPE_KEY] replaces plaintext
|
|
39
|
+
sinkToCustomerVisibleLog(sanitized);
|
|
40
|
+
|
|
41
|
+
// Rotate:
|
|
42
|
+
const next = await broker.rotate('STRIPE_KEY');
|
|
43
|
+
notifyDependents(next.fingerprint); // tell consumers a new key is in cache
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## v0.0.1 surface
|
|
47
|
+
|
|
48
|
+
| API | Purpose |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `createSecretBroker(options)` | Factory. Returns a `SecretBroker`. |
|
|
51
|
+
| `broker.resolve(name)` | Returns `{ value, fingerprint } | null`. Uses cache within `cacheTtlMs`. |
|
|
52
|
+
| `broker.fingerprint(value)` | Pure helper — sha256 prefix of any string. No adapter call. |
|
|
53
|
+
| `broker.redact(text)` | Rewrite arbitrary text, replacing every cached value (longer-first) with `[REDACTED:NAME]`. Skips values shorter than `redactionMinLength`. |
|
|
54
|
+
| `broker.rotate(name)` | Calls `adapter.rotate?`, caches the result, returns it. Throws if the adapter doesn't support rotation. |
|
|
55
|
+
| `broker.invalidate(name?)` | Clear one entry or the whole cache. |
|
|
56
|
+
| `broker.dispose()` | Tear down — clears cache; subsequent resolves return `null`. |
|
|
57
|
+
|
|
58
|
+
### Bundled adapters
|
|
59
|
+
|
|
60
|
+
| Adapter | Use |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `inMemoryAdapter({ initial?, rotate? })` | Tests, dev, and starter templates. Supports every operation. Default rotate = random base36. |
|
|
63
|
+
| `envAdapter({ prefix?, env? })` | Reads `process.env` (or any injected `env` map). Prefix-scoped to avoid leaking unrelated env vars via `list`. Read-only. |
|
|
64
|
+
| `compositeAdapter([...])` | Fan-out / fallback. `fetch` falls through; writes go to the first writeable adapter. |
|
|
65
|
+
|
|
66
|
+
AWS Secrets Manager / HashiCorp Vault / Doppler / Infisical / GCP Secret
|
|
67
|
+
Manager / Azure Key Vault adapters ship later as siblings — they're the
|
|
68
|
+
ones with real auth surface, so they don't belong in v0.0.1.
|
|
69
|
+
|
|
70
|
+
### Audit
|
|
71
|
+
|
|
72
|
+
Every `resolve`, `rotate`, and `invalidate` fires the `audit` hook with
|
|
73
|
+
one of:
|
|
74
|
+
|
|
75
|
+
- `{ event: 'resolve.hit', name, fingerprint, at }`
|
|
76
|
+
- `{ event: 'resolve.miss', name, fingerprint?, at }` — fingerprint present when the miss turned into a cache write
|
|
77
|
+
- `{ event: 'resolve.error', name, error, at }` — adapter threw
|
|
78
|
+
- `{ event: 'rotate', name, fingerprint, at }`
|
|
79
|
+
- `{ event: 'invalidate', name, at }` — `name` is `null` for a full clear
|
|
80
|
+
|
|
81
|
+
A throwing or rejecting hook is logged + discarded. The broker is on the
|
|
82
|
+
hot path for every credential lookup — one broken audit sink must not
|
|
83
|
+
take everything down.
|
|
84
|
+
|
|
85
|
+
### Why fingerprints
|
|
86
|
+
|
|
87
|
+
`broker.fingerprint(value)` is a deterministic sha256 prefix. Two purposes:
|
|
88
|
+
|
|
89
|
+
- **Logs.** Trace records can say "served credentials/abc12345" without
|
|
90
|
+
leaking the secret; if the same fingerprint shows up across two requests
|
|
91
|
+
you know they used the same key.
|
|
92
|
+
- **Webhook verification.** Compare an inbound HMAC hex against
|
|
93
|
+
`fingerprint(expectedSecret)` to confirm rotation propagation without
|
|
94
|
+
ever putting the secret next to the comparison.
|
|
95
|
+
|
|
96
|
+
It is NOT a security construct — 8 hex chars has only 32 bits of entropy.
|
|
97
|
+
Treat it as a tag, not a token.
|
|
98
|
+
|
|
99
|
+
## What v0.0.1 does NOT include
|
|
100
|
+
|
|
101
|
+
- Vendored AWS / Vault / Doppler / Infisical adapters (siblings, later).
|
|
102
|
+
- Encrypted on-disk cache.
|
|
103
|
+
- Cross-process secret sharing.
|
|
104
|
+
- Streaming key-rotation propagation to downstream sandboxes (caller wires that on top of `rotate`'s return).
|
|
105
|
+
|
|
106
|
+
## Architectural role
|
|
107
|
+
|
|
108
|
+
- **`@absolutejs/sync`** — `bridgeFetch.authorization()` and `unsafeHost` host functions ask the broker for credentials per call. The plaintext never crosses the sandbox boundary.
|
|
109
|
+
- **`@absolutejs/runtime`** — passes the broker into the per-tenant process via the `env` it injects (or via a side-channel; the broker doesn't care).
|
|
110
|
+
- **`@absolutejs/router`** — no direct relationship; runs upstream of the broker.
|
|
111
|
+
- **`@absolutejs/metering`** — `audit` can sink directly into the metering pipeline so credential lookups are billable observability events.
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
BSL 1.1 with a named carveout for the hosted secrets-management / credential-broker / runtime-secrets-injection category (AWS Secrets Manager, HashiCorp Vault Cloud, Doppler, 1Password Secrets, Infisical, GCP Secret Manager, Azure Key Vault, Akeyless, Cloudflare Workers Secrets, Vercel Environment Variables as a product). See [LICENSE](./LICENSE). Change Date: 4 years from first release; Change License: Apache 2.0.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @absolutejs/secrets — host-side secret broker for multi-tenant Bun
|
|
3
|
+
* runtimes.
|
|
4
|
+
*
|
|
5
|
+
* Three responsibilities, kept narrow on purpose:
|
|
6
|
+
*
|
|
7
|
+
* 1. **Resolve.** A pluggable adapter fetches a secret by name. The broker
|
|
8
|
+
* caches the answer, hands the caller back a `{ value, fingerprint }`
|
|
9
|
+
* pair (fingerprint is a sha256 prefix safe to put in logs), and fires
|
|
10
|
+
* an audit event for every lookup.
|
|
11
|
+
* 2. **Redact.** Walks every known cached secret out of an arbitrary string
|
|
12
|
+
* before it leaves the host (e.g. an error message that contains the
|
|
13
|
+
* leaked API key the host call just made). The replacement is
|
|
14
|
+
* `[REDACTED:name]`; matches shorter than `redactionMinLength` are
|
|
15
|
+
* skipped to avoid blanking coincidental short tokens.
|
|
16
|
+
* 3. **Rotate.** Delegates to `adapter.rotate?(name)` if the adapter
|
|
17
|
+
* supports it, invalidates the cache entry. Caller is expected to
|
|
18
|
+
* re-distribute to dependent surfaces.
|
|
19
|
+
*
|
|
20
|
+
* v0.0.1 ships three adapters: `inMemoryAdapter`, `envAdapter`,
|
|
21
|
+
* `compositeAdapter`. AWS Secrets Manager / Vault / Doppler / Infisical
|
|
22
|
+
* adapters ship later as siblings.
|
|
23
|
+
*
|
|
24
|
+
* The broker is bun/elysia-agnostic — same posture as router + meter.
|
|
25
|
+
*/
|
|
26
|
+
export type SecretValue = {
|
|
27
|
+
/** The plaintext secret. Treat as poison: never log, never serialize. */
|
|
28
|
+
value: string;
|
|
29
|
+
/**
|
|
30
|
+
* Short sha256-derived id (first 8 hex chars). Stable across calls for
|
|
31
|
+
* the same value; safe to print in logs and traces. Useful for "which
|
|
32
|
+
* version of the key did this request use?" diagnostics without leaking.
|
|
33
|
+
*/
|
|
34
|
+
fingerprint: string;
|
|
35
|
+
};
|
|
36
|
+
export type SecretAdapter = {
|
|
37
|
+
/** Return the plaintext value for a name, or `null` if not stored here. */
|
|
38
|
+
fetch: (name: string) => Promise<string | null>;
|
|
39
|
+
/** Write a value. Optional — adapters may be read-only. */
|
|
40
|
+
put?: (name: string, value: string) => Promise<void>;
|
|
41
|
+
/** Delete a value. Optional. */
|
|
42
|
+
remove?: (name: string) => Promise<void>;
|
|
43
|
+
/** Rotate a value; return the NEW plaintext. Optional. */
|
|
44
|
+
rotate?: (name: string) => Promise<string>;
|
|
45
|
+
/** Enumerate names. Optional; default omitted to avoid leaking the index. */
|
|
46
|
+
list?: () => Promise<string[]>;
|
|
47
|
+
};
|
|
48
|
+
export type AuditEvent = {
|
|
49
|
+
event: 'resolve.hit';
|
|
50
|
+
name: string;
|
|
51
|
+
fingerprint: string;
|
|
52
|
+
at: number;
|
|
53
|
+
} | {
|
|
54
|
+
event: 'resolve.miss';
|
|
55
|
+
name: string;
|
|
56
|
+
fingerprint?: string;
|
|
57
|
+
at: number;
|
|
58
|
+
} | {
|
|
59
|
+
event: 'resolve.error';
|
|
60
|
+
name: string;
|
|
61
|
+
error: string;
|
|
62
|
+
at: number;
|
|
63
|
+
} | {
|
|
64
|
+
event: 'rotate';
|
|
65
|
+
name: string;
|
|
66
|
+
fingerprint: string;
|
|
67
|
+
at: number;
|
|
68
|
+
} | {
|
|
69
|
+
event: 'invalidate';
|
|
70
|
+
name: string | null;
|
|
71
|
+
at: number;
|
|
72
|
+
};
|
|
73
|
+
export type AuditHook = (event: AuditEvent) => void | Promise<void>;
|
|
74
|
+
export type SecretBrokerOptions = {
|
|
75
|
+
/** The adapter the broker delegates fetch / rotate / put to. */
|
|
76
|
+
adapter: SecretAdapter;
|
|
77
|
+
/** Audit hook fired on every resolve / rotate / invalidate. */
|
|
78
|
+
audit?: AuditHook;
|
|
79
|
+
/**
|
|
80
|
+
* How long a cached secret stays fresh, in ms. After this, the next
|
|
81
|
+
* resolve re-hits the adapter. Default 60_000 (1 minute). Set to
|
|
82
|
+
* `Infinity` to disable TTL — only `rotate` / `invalidate` evict.
|
|
83
|
+
*/
|
|
84
|
+
cacheTtlMs?: number;
|
|
85
|
+
/**
|
|
86
|
+
* Minimum length a cached value must have before `redact` will rewrite
|
|
87
|
+
* occurrences of it in arbitrary text. Default 8 — short values risk
|
|
88
|
+
* blanking out coincidental matches (e.g. a short password "abc1"
|
|
89
|
+
* appearing as a substring of unrelated text).
|
|
90
|
+
*/
|
|
91
|
+
redactionMinLength?: number;
|
|
92
|
+
/** Override `Date.now` for tests. */
|
|
93
|
+
clock?: () => number;
|
|
94
|
+
};
|
|
95
|
+
export type SecretBroker = {
|
|
96
|
+
/**
|
|
97
|
+
* Resolve a secret by name. Returns `null` if the adapter reports
|
|
98
|
+
* no value. Caches the answer for `cacheTtlMs`.
|
|
99
|
+
*/
|
|
100
|
+
resolve: (name: string) => Promise<SecretValue | null>;
|
|
101
|
+
/**
|
|
102
|
+
* Returns the fingerprint of a value WITHOUT touching the adapter.
|
|
103
|
+
* Useful for hashing a value the caller already has — e.g. a webhook
|
|
104
|
+
* payload — to compare against an audit log.
|
|
105
|
+
*/
|
|
106
|
+
fingerprint: (value: string) => string;
|
|
107
|
+
/**
|
|
108
|
+
* Replace every cached secret value found in `text` with
|
|
109
|
+
* `[REDACTED:name]`. Returns the rewritten text. Subjects shorter than
|
|
110
|
+
* `redactionMinLength` are skipped.
|
|
111
|
+
*/
|
|
112
|
+
redact: (text: string) => string;
|
|
113
|
+
/**
|
|
114
|
+
* Rotate a secret. Calls `adapter.rotate(name)`, invalidates the cache,
|
|
115
|
+
* returns the new `{ value, fingerprint }`. Throws if the adapter does
|
|
116
|
+
* not support rotation.
|
|
117
|
+
*/
|
|
118
|
+
rotate: (name: string) => Promise<SecretValue>;
|
|
119
|
+
/**
|
|
120
|
+
* Invalidate one cache entry, or the whole cache when `name` is omitted.
|
|
121
|
+
*/
|
|
122
|
+
invalidate: (name?: string) => void;
|
|
123
|
+
/** Tear down the broker — clears the cache; further resolves still hit the adapter. */
|
|
124
|
+
dispose: () => void;
|
|
125
|
+
};
|
|
126
|
+
export type InMemoryAdapterOptions = {
|
|
127
|
+
initial?: Record<string, string>;
|
|
128
|
+
/** Override the rotation strategy. Default = random 32-char base36 string. */
|
|
129
|
+
rotate?: (name: string, previous: string | null) => string;
|
|
130
|
+
};
|
|
131
|
+
export declare const inMemoryAdapter: (options?: InMemoryAdapterOptions) => SecretAdapter;
|
|
132
|
+
export type EnvAdapterOptions = {
|
|
133
|
+
/**
|
|
134
|
+
* If set, lookups are prefixed before reading from env. e.g.
|
|
135
|
+
* `prefix: 'ABS_SECRET_'` and `resolve('STRIPE_KEY')` reads `ABS_SECRET_STRIPE_KEY`.
|
|
136
|
+
* Default `''` (no prefix).
|
|
137
|
+
*/
|
|
138
|
+
prefix?: string;
|
|
139
|
+
/** The env object to read from. Default `process.env`. */
|
|
140
|
+
env?: Record<string, string | undefined>;
|
|
141
|
+
};
|
|
142
|
+
export declare const envAdapter: (options?: EnvAdapterOptions) => SecretAdapter;
|
|
143
|
+
/**
|
|
144
|
+
* Compose adapters: `fetch` falls through to the first non-null result;
|
|
145
|
+
* `put` / `rotate` / `remove` go to the first adapter that implements them.
|
|
146
|
+
*/
|
|
147
|
+
export declare const compositeAdapter: (adapters: SecretAdapter[]) => SecretAdapter;
|
|
148
|
+
export declare const createSecretBroker: (options: SecretBrokerOptions) => SecretBroker;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/index.ts
|
|
3
|
+
var HEX = "0123456789abcdef";
|
|
4
|
+
var sha256Hex = (input) => {
|
|
5
|
+
return sha256(input);
|
|
6
|
+
};
|
|
7
|
+
var ROUND_CONSTANTS = new Uint32Array([
|
|
8
|
+
1116352408,
|
|
9
|
+
1899447441,
|
|
10
|
+
3049323471,
|
|
11
|
+
3921009573,
|
|
12
|
+
961987163,
|
|
13
|
+
1508970993,
|
|
14
|
+
2453635748,
|
|
15
|
+
2870763221,
|
|
16
|
+
3624381080,
|
|
17
|
+
310598401,
|
|
18
|
+
607225278,
|
|
19
|
+
1426881987,
|
|
20
|
+
1925078388,
|
|
21
|
+
2162078206,
|
|
22
|
+
2614888103,
|
|
23
|
+
3248222580,
|
|
24
|
+
3835390401,
|
|
25
|
+
4022224774,
|
|
26
|
+
264347078,
|
|
27
|
+
604807628,
|
|
28
|
+
770255983,
|
|
29
|
+
1249150122,
|
|
30
|
+
1555081692,
|
|
31
|
+
1996064986,
|
|
32
|
+
2554220882,
|
|
33
|
+
2821834349,
|
|
34
|
+
2952996808,
|
|
35
|
+
3210313671,
|
|
36
|
+
3336571891,
|
|
37
|
+
3584528711,
|
|
38
|
+
113926993,
|
|
39
|
+
338241895,
|
|
40
|
+
666307205,
|
|
41
|
+
773529912,
|
|
42
|
+
1294757372,
|
|
43
|
+
1396182291,
|
|
44
|
+
1695183700,
|
|
45
|
+
1986661051,
|
|
46
|
+
2177026350,
|
|
47
|
+
2456956037,
|
|
48
|
+
2730485921,
|
|
49
|
+
2820302411,
|
|
50
|
+
3259730800,
|
|
51
|
+
3345764771,
|
|
52
|
+
3516065817,
|
|
53
|
+
3600352804,
|
|
54
|
+
4094571909,
|
|
55
|
+
275423344,
|
|
56
|
+
430227734,
|
|
57
|
+
506948616,
|
|
58
|
+
659060556,
|
|
59
|
+
883997877,
|
|
60
|
+
958139571,
|
|
61
|
+
1322822218,
|
|
62
|
+
1537002063,
|
|
63
|
+
1747873779,
|
|
64
|
+
1955562222,
|
|
65
|
+
2024104815,
|
|
66
|
+
2227730452,
|
|
67
|
+
2361852424,
|
|
68
|
+
2428436474,
|
|
69
|
+
2756734187,
|
|
70
|
+
3204031479,
|
|
71
|
+
3329325298
|
|
72
|
+
]);
|
|
73
|
+
var sha256 = (input) => {
|
|
74
|
+
const bytes = new TextEncoder().encode(input);
|
|
75
|
+
const bitLength = bytes.length * 8;
|
|
76
|
+
const padLength = (bytes.length + 9 + 63 & ~63) - bytes.length;
|
|
77
|
+
const padded = new Uint8Array(bytes.length + padLength);
|
|
78
|
+
padded.set(bytes);
|
|
79
|
+
padded[bytes.length] = 128;
|
|
80
|
+
const view = new DataView(padded.buffer);
|
|
81
|
+
view.setUint32(padded.length - 4, bitLength >>> 0);
|
|
82
|
+
view.setUint32(padded.length - 8, Math.floor(bitLength / 4294967296));
|
|
83
|
+
let h0 = 1779033703, h1 = 3144134277, h2 = 1013904242, h3 = 2773480762;
|
|
84
|
+
let h4 = 1359893119, h5 = 2600822924, h6 = 528734635, h7 = 1541459225;
|
|
85
|
+
const w = new Uint32Array(64);
|
|
86
|
+
for (let i = 0;i < padded.length; i += 64) {
|
|
87
|
+
for (let t = 0;t < 16; t++) {
|
|
88
|
+
w[t] = view.getUint32(i + t * 4);
|
|
89
|
+
}
|
|
90
|
+
for (let t = 16;t < 64; t++) {
|
|
91
|
+
const w15 = w[t - 15];
|
|
92
|
+
const w2 = w[t - 2];
|
|
93
|
+
const s0 = (w15 >>> 7 | w15 << 25) ^ (w15 >>> 18 | w15 << 14) ^ w15 >>> 3;
|
|
94
|
+
const s1 = (w2 >>> 17 | w2 << 15) ^ (w2 >>> 19 | w2 << 13) ^ w2 >>> 10;
|
|
95
|
+
w[t] = w[t - 16] + s0 + w[t - 7] + s1 >>> 0;
|
|
96
|
+
}
|
|
97
|
+
let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;
|
|
98
|
+
for (let t = 0;t < 64; t++) {
|
|
99
|
+
const S1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
|
|
100
|
+
const ch = e & f ^ ~e & g;
|
|
101
|
+
const T1 = h + S1 + ch + ROUND_CONSTANTS[t] + w[t] >>> 0;
|
|
102
|
+
const S0 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10);
|
|
103
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
104
|
+
const T2 = S0 + maj >>> 0;
|
|
105
|
+
h = g;
|
|
106
|
+
g = f;
|
|
107
|
+
f = e;
|
|
108
|
+
e = d + T1 >>> 0;
|
|
109
|
+
d = c;
|
|
110
|
+
c = b;
|
|
111
|
+
b = a;
|
|
112
|
+
a = T1 + T2 >>> 0;
|
|
113
|
+
}
|
|
114
|
+
h0 = h0 + a >>> 0;
|
|
115
|
+
h1 = h1 + b >>> 0;
|
|
116
|
+
h2 = h2 + c >>> 0;
|
|
117
|
+
h3 = h3 + d >>> 0;
|
|
118
|
+
h4 = h4 + e >>> 0;
|
|
119
|
+
h5 = h5 + f >>> 0;
|
|
120
|
+
h6 = h6 + g >>> 0;
|
|
121
|
+
h7 = h7 + h >>> 0;
|
|
122
|
+
}
|
|
123
|
+
const toHex = (n) => {
|
|
124
|
+
let result = "";
|
|
125
|
+
for (let i = 7;i >= 0; i--) {
|
|
126
|
+
result += HEX[n >>> i * 4 & 15];
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
};
|
|
130
|
+
return toHex(h0) + toHex(h1) + toHex(h2) + toHex(h3) + toHex(h4) + toHex(h5) + toHex(h6) + toHex(h7);
|
|
131
|
+
};
|
|
132
|
+
var fingerprintOf = (value) => sha256Hex(value).slice(0, 8);
|
|
133
|
+
var randomBase36 = (length) => {
|
|
134
|
+
let out = "";
|
|
135
|
+
while (out.length < length) {
|
|
136
|
+
out += Math.random().toString(36).slice(2);
|
|
137
|
+
}
|
|
138
|
+
return out.slice(0, length);
|
|
139
|
+
};
|
|
140
|
+
var inMemoryAdapter = (options = {}) => {
|
|
141
|
+
const store = new Map;
|
|
142
|
+
for (const [k, v] of Object.entries(options.initial ?? {}))
|
|
143
|
+
store.set(k, v);
|
|
144
|
+
const rotate = options.rotate ?? (() => randomBase36(32));
|
|
145
|
+
return {
|
|
146
|
+
fetch: async (name) => store.get(name) ?? null,
|
|
147
|
+
list: async () => Array.from(store.keys()),
|
|
148
|
+
put: async (name, value) => {
|
|
149
|
+
store.set(name, value);
|
|
150
|
+
},
|
|
151
|
+
remove: async (name) => {
|
|
152
|
+
store.delete(name);
|
|
153
|
+
},
|
|
154
|
+
rotate: async (name) => {
|
|
155
|
+
const next = rotate(name, store.get(name) ?? null);
|
|
156
|
+
store.set(name, next);
|
|
157
|
+
return next;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
var envAdapter = (options = {}) => {
|
|
162
|
+
const prefix = options.prefix ?? "";
|
|
163
|
+
const env = options.env ?? globalThis.process?.env ?? {};
|
|
164
|
+
return {
|
|
165
|
+
fetch: async (name) => {
|
|
166
|
+
const key = `${prefix}${name}`;
|
|
167
|
+
const value = env[key];
|
|
168
|
+
return value === undefined ? null : value;
|
|
169
|
+
},
|
|
170
|
+
list: async () => {
|
|
171
|
+
if (!prefix)
|
|
172
|
+
return Object.keys(env);
|
|
173
|
+
const matches = [];
|
|
174
|
+
for (const key of Object.keys(env)) {
|
|
175
|
+
if (key.startsWith(prefix))
|
|
176
|
+
matches.push(key.slice(prefix.length));
|
|
177
|
+
}
|
|
178
|
+
return matches;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
var compositeAdapter = (adapters) => {
|
|
183
|
+
const firstWith = (method) => adapters.find((adapter) => adapter[method] !== undefined);
|
|
184
|
+
return {
|
|
185
|
+
fetch: async (name) => {
|
|
186
|
+
for (const adapter of adapters) {
|
|
187
|
+
const value = await adapter.fetch(name);
|
|
188
|
+
if (value !== null)
|
|
189
|
+
return value;
|
|
190
|
+
}
|
|
191
|
+
return null;
|
|
192
|
+
},
|
|
193
|
+
list: async () => {
|
|
194
|
+
const seen = new Set;
|
|
195
|
+
for (const adapter of adapters) {
|
|
196
|
+
if (!adapter.list)
|
|
197
|
+
continue;
|
|
198
|
+
for (const name of await adapter.list())
|
|
199
|
+
seen.add(name);
|
|
200
|
+
}
|
|
201
|
+
return Array.from(seen);
|
|
202
|
+
},
|
|
203
|
+
put: async (name, value) => {
|
|
204
|
+
const target = firstWith("put");
|
|
205
|
+
if (!target?.put)
|
|
206
|
+
throw new Error("No adapter in the composite supports put()");
|
|
207
|
+
await target.put(name, value);
|
|
208
|
+
},
|
|
209
|
+
remove: async (name) => {
|
|
210
|
+
const target = firstWith("remove");
|
|
211
|
+
if (!target?.remove)
|
|
212
|
+
throw new Error("No adapter in the composite supports remove()");
|
|
213
|
+
await target.remove(name);
|
|
214
|
+
},
|
|
215
|
+
rotate: async (name) => {
|
|
216
|
+
const target = firstWith("rotate");
|
|
217
|
+
if (!target?.rotate)
|
|
218
|
+
throw new Error("No adapter in the composite supports rotate()");
|
|
219
|
+
return target.rotate(name);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
};
|
|
223
|
+
var createSecretBroker = (options) => {
|
|
224
|
+
const clock = options.clock ?? Date.now;
|
|
225
|
+
const ttl = options.cacheTtlMs ?? 60000;
|
|
226
|
+
const minLen = options.redactionMinLength ?? 8;
|
|
227
|
+
const audit = options.audit;
|
|
228
|
+
const cache = new Map;
|
|
229
|
+
let disposed = false;
|
|
230
|
+
const fireAudit = (event) => {
|
|
231
|
+
if (!audit)
|
|
232
|
+
return;
|
|
233
|
+
try {
|
|
234
|
+
const result = audit(event);
|
|
235
|
+
if (result && typeof result.then === "function") {
|
|
236
|
+
result.catch((error) => {
|
|
237
|
+
console.error("[secrets] audit hook rejected:", error);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.error("[secrets] audit hook threw:", error);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
const cacheEntry = (name, value, now) => {
|
|
245
|
+
const entry = {
|
|
246
|
+
fingerprint: fingerprintOf(value),
|
|
247
|
+
storedAt: now,
|
|
248
|
+
value
|
|
249
|
+
};
|
|
250
|
+
cache.set(name, entry);
|
|
251
|
+
return entry;
|
|
252
|
+
};
|
|
253
|
+
const resolve = async (name) => {
|
|
254
|
+
if (disposed)
|
|
255
|
+
return null;
|
|
256
|
+
const now = clock();
|
|
257
|
+
const cached = cache.get(name);
|
|
258
|
+
if (cached && now - cached.storedAt < ttl) {
|
|
259
|
+
fireAudit({ at: now, event: "resolve.hit", fingerprint: cached.fingerprint, name });
|
|
260
|
+
return { fingerprint: cached.fingerprint, value: cached.value };
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
const value = await options.adapter.fetch(name);
|
|
264
|
+
if (value === null) {
|
|
265
|
+
fireAudit({ at: now, event: "resolve.miss", name });
|
|
266
|
+
cache.delete(name);
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
const entry = cacheEntry(name, value, now);
|
|
270
|
+
fireAudit({ at: now, event: "resolve.miss", fingerprint: entry.fingerprint, name });
|
|
271
|
+
return { fingerprint: entry.fingerprint, value: entry.value };
|
|
272
|
+
} catch (error) {
|
|
273
|
+
fireAudit({
|
|
274
|
+
at: now,
|
|
275
|
+
error: error instanceof Error ? error.message : String(error),
|
|
276
|
+
event: "resolve.error",
|
|
277
|
+
name
|
|
278
|
+
});
|
|
279
|
+
throw error;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
const rotate = async (name) => {
|
|
283
|
+
if (disposed)
|
|
284
|
+
throw new Error("Broker is disposed");
|
|
285
|
+
if (!options.adapter.rotate) {
|
|
286
|
+
throw new Error("Adapter does not support rotate()");
|
|
287
|
+
}
|
|
288
|
+
const next = await options.adapter.rotate(name);
|
|
289
|
+
const now = clock();
|
|
290
|
+
const entry = cacheEntry(name, next, now);
|
|
291
|
+
fireAudit({ at: now, event: "rotate", fingerprint: entry.fingerprint, name });
|
|
292
|
+
return { fingerprint: entry.fingerprint, value: entry.value };
|
|
293
|
+
};
|
|
294
|
+
const invalidate = (name) => {
|
|
295
|
+
if (name === undefined) {
|
|
296
|
+
cache.clear();
|
|
297
|
+
} else {
|
|
298
|
+
cache.delete(name);
|
|
299
|
+
}
|
|
300
|
+
fireAudit({ at: clock(), event: "invalidate", name: name ?? null });
|
|
301
|
+
};
|
|
302
|
+
const redact = (text) => {
|
|
303
|
+
if (text.length === 0 || cache.size === 0)
|
|
304
|
+
return text;
|
|
305
|
+
const ordered = Array.from(cache.entries()).filter(([, entry]) => entry.value.length >= minLen).sort(([, a], [, b]) => b.value.length - a.value.length);
|
|
306
|
+
let out = text;
|
|
307
|
+
for (const [name, entry] of ordered) {
|
|
308
|
+
if (!out.includes(entry.value))
|
|
309
|
+
continue;
|
|
310
|
+
out = out.split(entry.value).join(`[REDACTED:${name}]`);
|
|
311
|
+
}
|
|
312
|
+
return out;
|
|
313
|
+
};
|
|
314
|
+
return {
|
|
315
|
+
dispose: () => {
|
|
316
|
+
disposed = true;
|
|
317
|
+
cache.clear();
|
|
318
|
+
},
|
|
319
|
+
fingerprint: fingerprintOf,
|
|
320
|
+
invalidate,
|
|
321
|
+
redact,
|
|
322
|
+
resolve,
|
|
323
|
+
rotate
|
|
324
|
+
};
|
|
325
|
+
};
|
|
326
|
+
export {
|
|
327
|
+
inMemoryAdapter,
|
|
328
|
+
envAdapter,
|
|
329
|
+
createSecretBroker,
|
|
330
|
+
compositeAdapter
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
//# debugId=EE866E47E57910C764756E2164756E21
|
|
334
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * @absolutejs/secrets — host-side secret broker for multi-tenant Bun\n * runtimes.\n *\n * Three responsibilities, kept narrow on purpose:\n *\n * 1. **Resolve.** A pluggable adapter fetches a secret by name. The broker\n * caches the answer, hands the caller back a `{ value, fingerprint }`\n * pair (fingerprint is a sha256 prefix safe to put in logs), and fires\n * an audit event for every lookup.\n * 2. **Redact.** Walks every known cached secret out of an arbitrary string\n * before it leaves the host (e.g. an error message that contains the\n * leaked API key the host call just made). The replacement is\n * `[REDACTED:name]`; matches shorter than `redactionMinLength` are\n * skipped to avoid blanking coincidental short tokens.\n * 3. **Rotate.** Delegates to `adapter.rotate?(name)` if the adapter\n * supports it, invalidates the cache entry. Caller is expected to\n * re-distribute to dependent surfaces.\n *\n * v0.0.1 ships three adapters: `inMemoryAdapter`, `envAdapter`,\n * `compositeAdapter`. AWS Secrets Manager / Vault / Doppler / Infisical\n * adapters ship later as siblings.\n *\n * The broker is bun/elysia-agnostic — same posture as router + meter.\n */\n\nexport type SecretValue = {\n\t/** The plaintext secret. Treat as poison: never log, never serialize. */\n\tvalue: string;\n\t/**\n\t * Short sha256-derived id (first 8 hex chars). Stable across calls for\n\t * the same value; safe to print in logs and traces. Useful for \"which\n\t * version of the key did this request use?\" diagnostics without leaking.\n\t */\n\tfingerprint: string;\n};\n\nexport type SecretAdapter = {\n\t/** Return the plaintext value for a name, or `null` if not stored here. */\n\tfetch: (name: string) => Promise<string | null>;\n\t/** Write a value. Optional — adapters may be read-only. */\n\tput?: (name: string, value: string) => Promise<void>;\n\t/** Delete a value. Optional. */\n\tremove?: (name: string) => Promise<void>;\n\t/** Rotate a value; return the NEW plaintext. Optional. */\n\trotate?: (name: string) => Promise<string>;\n\t/** Enumerate names. Optional; default omitted to avoid leaking the index. */\n\tlist?: () => Promise<string[]>;\n};\n\nexport type AuditEvent =\n\t| { event: 'resolve.hit'; name: string; fingerprint: string; at: number }\n\t| { event: 'resolve.miss'; name: string; fingerprint?: string; at: number }\n\t| { event: 'resolve.error'; name: string; error: string; at: number }\n\t| { event: 'rotate'; name: string; fingerprint: string; at: number }\n\t| { event: 'invalidate'; name: string | null; at: number };\n\nexport type AuditHook = (event: AuditEvent) => void | Promise<void>;\n\nexport type SecretBrokerOptions = {\n\t/** The adapter the broker delegates fetch / rotate / put to. */\n\tadapter: SecretAdapter;\n\t/** Audit hook fired on every resolve / rotate / invalidate. */\n\taudit?: AuditHook;\n\t/**\n\t * How long a cached secret stays fresh, in ms. After this, the next\n\t * resolve re-hits the adapter. Default 60_000 (1 minute). Set to\n\t * `Infinity` to disable TTL — only `rotate` / `invalidate` evict.\n\t */\n\tcacheTtlMs?: number;\n\t/**\n\t * Minimum length a cached value must have before `redact` will rewrite\n\t * occurrences of it in arbitrary text. Default 8 — short values risk\n\t * blanking out coincidental matches (e.g. a short password \"abc1\"\n\t * appearing as a substring of unrelated text).\n\t */\n\tredactionMinLength?: number;\n\t/** Override `Date.now` for tests. */\n\tclock?: () => number;\n};\n\nexport type SecretBroker = {\n\t/**\n\t * Resolve a secret by name. Returns `null` if the adapter reports\n\t * no value. Caches the answer for `cacheTtlMs`.\n\t */\n\tresolve: (name: string) => Promise<SecretValue | null>;\n\t/**\n\t * Returns the fingerprint of a value WITHOUT touching the adapter.\n\t * Useful for hashing a value the caller already has — e.g. a webhook\n\t * payload — to compare against an audit log.\n\t */\n\tfingerprint: (value: string) => string;\n\t/**\n\t * Replace every cached secret value found in `text` with\n\t * `[REDACTED:name]`. Returns the rewritten text. Subjects shorter than\n\t * `redactionMinLength` are skipped.\n\t */\n\tredact: (text: string) => string;\n\t/**\n\t * Rotate a secret. Calls `adapter.rotate(name)`, invalidates the cache,\n\t * returns the new `{ value, fingerprint }`. Throws if the adapter does\n\t * not support rotation.\n\t */\n\trotate: (name: string) => Promise<SecretValue>;\n\t/**\n\t * Invalidate one cache entry, or the whole cache when `name` is omitted.\n\t */\n\tinvalidate: (name?: string) => void;\n\t/** Tear down the broker — clears the cache; further resolves still hit the adapter. */\n\tdispose: () => void;\n};\n\n// -----------------------------------------------------------------------------\n// Fingerprint\n// -----------------------------------------------------------------------------\n\nconst HEX = '0123456789abcdef';\n\nconst sha256Hex = (input: string): string => {\n\t// 2026-era Bun: `crypto.subtle.digest` is the portable path. Synchronous\n\t// path via `Bun.CryptoHasher` is faster but requires Bun globals; we use\n\t// subtle to keep the broker runtime-agnostic at the cost of being async.\n\t// For the `fingerprint(value)` *public* method we want a synchronous\n\t// answer — so we use a tiny in-house sha256. It's slower than the native\n\t// digest but only runs on the secret value (small, ~ < 1KB), once per\n\t// distinct value over the broker's lifetime (memoized in the cache entry).\n\treturn sha256(input);\n};\n\n// Pure JS sha256, NIST FIPS 180-4. Small + dependency-free. Returns lowercase hex.\nconst ROUND_CONSTANTS = new Uint32Array([\n\t0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,\n\t0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,\n\t0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,\n\t0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n\t0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,\n\t0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,\n\t0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,\n\t0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n\t0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,\n\t0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,\n\t0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n]);\n\nconst sha256 = (input: string): string => {\n\tconst bytes = new TextEncoder().encode(input);\n\tconst bitLength = bytes.length * 8;\n\tconst padLength = ((bytes.length + 9 + 63) & ~63) - bytes.length;\n\tconst padded = new Uint8Array(bytes.length + padLength);\n\tpadded.set(bytes);\n\tpadded[bytes.length] = 0x80;\n\tconst view = new DataView(padded.buffer);\n\tview.setUint32(padded.length - 4, bitLength >>> 0);\n\tview.setUint32(padded.length - 8, Math.floor(bitLength / 0x1_0000_0000));\n\n\tlet h0 = 0x6a09e667, h1 = 0xbb67ae85, h2 = 0x3c6ef372, h3 = 0xa54ff53a;\n\tlet h4 = 0x510e527f, h5 = 0x9b05688c, h6 = 0x1f83d9ab, h7 = 0x5be0cd19;\n\tconst w = new Uint32Array(64);\n\tfor (let i = 0; i < padded.length; i += 64) {\n\t\tfor (let t = 0; t < 16; t++) {\n\t\t\tw[t] = view.getUint32(i + t * 4);\n\t\t}\n\t\tfor (let t = 16; t < 64; t++) {\n\t\t\tconst w15 = w[t - 15]!;\n\t\t\tconst w2 = w[t - 2]!;\n\t\t\tconst s0 = ((w15 >>> 7) | (w15 << 25)) ^ ((w15 >>> 18) | (w15 << 14)) ^ (w15 >>> 3);\n\t\t\tconst s1 = ((w2 >>> 17) | (w2 << 15)) ^ ((w2 >>> 19) | (w2 << 13)) ^ (w2 >>> 10);\n\t\t\tw[t] = (w[t - 16]! + s0 + w[t - 7]! + s1) >>> 0;\n\t\t}\n\t\tlet a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;\n\t\tfor (let t = 0; t < 64; t++) {\n\t\t\tconst S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));\n\t\t\tconst ch = (e & f) ^ (~e & g);\n\t\t\tconst T1 = (h + S1 + ch + ROUND_CONSTANTS[t]! + w[t]!) >>> 0;\n\t\t\tconst S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));\n\t\t\tconst maj = (a & b) ^ (a & c) ^ (b & c);\n\t\t\tconst T2 = (S0 + maj) >>> 0;\n\t\t\th = g;\n\t\t\tg = f;\n\t\t\tf = e;\n\t\t\te = (d + T1) >>> 0;\n\t\t\td = c;\n\t\t\tc = b;\n\t\t\tb = a;\n\t\t\ta = (T1 + T2) >>> 0;\n\t\t}\n\t\th0 = (h0 + a) >>> 0;\n\t\th1 = (h1 + b) >>> 0;\n\t\th2 = (h2 + c) >>> 0;\n\t\th3 = (h3 + d) >>> 0;\n\t\th4 = (h4 + e) >>> 0;\n\t\th5 = (h5 + f) >>> 0;\n\t\th6 = (h6 + g) >>> 0;\n\t\th7 = (h7 + h) >>> 0;\n\t}\n\tconst toHex = (n: number): string => {\n\t\tlet result = '';\n\t\tfor (let i = 7; i >= 0; i--) {\n\t\t\tresult += HEX[(n >>> (i * 4)) & 0xf];\n\t\t}\n\t\treturn result;\n\t};\n\treturn toHex(h0) + toHex(h1) + toHex(h2) + toHex(h3) + toHex(h4) + toHex(h5) + toHex(h6) + toHex(h7);\n};\n\nconst fingerprintOf = (value: string): string => sha256Hex(value).slice(0, 8);\n\n// -----------------------------------------------------------------------------\n// Bundled adapters\n// -----------------------------------------------------------------------------\n\nexport type InMemoryAdapterOptions = {\n\tinitial?: Record<string, string>;\n\t/** Override the rotation strategy. Default = random 32-char base36 string. */\n\trotate?: (name: string, previous: string | null) => string;\n};\n\nconst randomBase36 = (length: number): string => {\n\tlet out = '';\n\twhile (out.length < length) {\n\t\tout += Math.random().toString(36).slice(2);\n\t}\n\treturn out.slice(0, length);\n};\n\nexport const inMemoryAdapter = (\n\toptions: InMemoryAdapterOptions = {},\n): SecretAdapter => {\n\tconst store = new Map<string, string>();\n\tfor (const [k, v] of Object.entries(options.initial ?? {})) store.set(k, v);\n\tconst rotate = options.rotate ?? (() => randomBase36(32));\n\treturn {\n\t\tfetch: async (name) => store.get(name) ?? null,\n\t\tlist: async () => Array.from(store.keys()),\n\t\tput: async (name, value) => { store.set(name, value); },\n\t\tremove: async (name) => { store.delete(name); },\n\t\trotate: async (name) => {\n\t\t\tconst next = rotate(name, store.get(name) ?? null);\n\t\t\tstore.set(name, next);\n\t\t\treturn next;\n\t\t},\n\t};\n};\n\nexport type EnvAdapterOptions = {\n\t/**\n\t * If set, lookups are prefixed before reading from env. e.g.\n\t * `prefix: 'ABS_SECRET_'` and `resolve('STRIPE_KEY')` reads `ABS_SECRET_STRIPE_KEY`.\n\t * Default `''` (no prefix).\n\t */\n\tprefix?: string;\n\t/** The env object to read from. Default `process.env`. */\n\tenv?: Record<string, string | undefined>;\n};\n\nexport const envAdapter = (options: EnvAdapterOptions = {}): SecretAdapter => {\n\tconst prefix = options.prefix ?? '';\n\tconst env = options.env ?? (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env ?? {};\n\treturn {\n\t\tfetch: async (name) => {\n\t\t\tconst key = `${prefix}${name}`;\n\t\t\tconst value = env[key];\n\t\t\treturn value === undefined ? null : value;\n\t\t},\n\t\tlist: async () => {\n\t\t\tif (!prefix) return Object.keys(env);\n\t\t\tconst matches: string[] = [];\n\t\t\tfor (const key of Object.keys(env)) {\n\t\t\t\tif (key.startsWith(prefix)) matches.push(key.slice(prefix.length));\n\t\t\t}\n\t\t\treturn matches;\n\t\t},\n\t};\n};\n\n/**\n * Compose adapters: `fetch` falls through to the first non-null result;\n * `put` / `rotate` / `remove` go to the first adapter that implements them.\n */\nexport const compositeAdapter = (adapters: SecretAdapter[]): SecretAdapter => {\n\tconst firstWith = <K extends keyof SecretAdapter>(method: K) =>\n\t\tadapters.find((adapter) => adapter[method] !== undefined);\n\treturn {\n\t\tfetch: async (name) => {\n\t\t\tfor (const adapter of adapters) {\n\t\t\t\tconst value = await adapter.fetch(name);\n\t\t\t\tif (value !== null) return value;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t\tlist: async () => {\n\t\t\tconst seen = new Set<string>();\n\t\t\tfor (const adapter of adapters) {\n\t\t\t\tif (!adapter.list) continue;\n\t\t\t\tfor (const name of await adapter.list()) seen.add(name);\n\t\t\t}\n\t\t\treturn Array.from(seen);\n\t\t},\n\t\tput: async (name, value) => {\n\t\t\tconst target = firstWith('put');\n\t\t\tif (!target?.put) throw new Error('No adapter in the composite supports put()');\n\t\t\tawait target.put(name, value);\n\t\t},\n\t\tremove: async (name) => {\n\t\t\tconst target = firstWith('remove');\n\t\t\tif (!target?.remove) throw new Error('No adapter in the composite supports remove()');\n\t\t\tawait target.remove(name);\n\t\t},\n\t\trotate: async (name) => {\n\t\t\tconst target = firstWith('rotate');\n\t\t\tif (!target?.rotate) throw new Error('No adapter in the composite supports rotate()');\n\t\t\treturn target.rotate(name);\n\t\t},\n\t};\n};\n\n// -----------------------------------------------------------------------------\n// Broker\n// -----------------------------------------------------------------------------\n\ntype CacheEntry = {\n\tvalue: string;\n\tfingerprint: string;\n\tstoredAt: number;\n};\n\nexport const createSecretBroker = (options: SecretBrokerOptions): SecretBroker => {\n\tconst clock = options.clock ?? Date.now;\n\tconst ttl = options.cacheTtlMs ?? 60_000;\n\tconst minLen = options.redactionMinLength ?? 8;\n\tconst audit = options.audit;\n\tconst cache = new Map<string, CacheEntry>();\n\tlet disposed = false;\n\n\tconst fireAudit = (event: AuditEvent) => {\n\t\tif (!audit) return;\n\t\ttry {\n\t\t\tconst result = audit(event);\n\t\t\tif (result && typeof (result as Promise<void>).then === 'function') {\n\t\t\t\t(result as Promise<void>).catch((error) => {\n\t\t\t\t\tconsole.error('[secrets] audit hook rejected:', error);\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error('[secrets] audit hook threw:', error);\n\t\t}\n\t};\n\n\tconst cacheEntry = (name: string, value: string, now: number): CacheEntry => {\n\t\tconst entry: CacheEntry = {\n\t\t\tfingerprint: fingerprintOf(value),\n\t\t\tstoredAt: now,\n\t\t\tvalue,\n\t\t};\n\t\tcache.set(name, entry);\n\t\treturn entry;\n\t};\n\n\tconst resolve: SecretBroker['resolve'] = async (name) => {\n\t\tif (disposed) return null;\n\t\tconst now = clock();\n\t\tconst cached = cache.get(name);\n\t\tif (cached && now - cached.storedAt < ttl) {\n\t\t\tfireAudit({ at: now, event: 'resolve.hit', fingerprint: cached.fingerprint, name });\n\t\t\treturn { fingerprint: cached.fingerprint, value: cached.value };\n\t\t}\n\t\ttry {\n\t\t\tconst value = await options.adapter.fetch(name);\n\t\t\tif (value === null) {\n\t\t\t\tfireAudit({ at: now, event: 'resolve.miss', name });\n\t\t\t\tcache.delete(name);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst entry = cacheEntry(name, value, now);\n\t\t\tfireAudit({ at: now, event: 'resolve.miss', fingerprint: entry.fingerprint, name });\n\t\t\treturn { fingerprint: entry.fingerprint, value: entry.value };\n\t\t} catch (error) {\n\t\t\tfireAudit({\n\t\t\t\tat: now,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\tevent: 'resolve.error',\n\t\t\t\tname,\n\t\t\t});\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\tconst rotate: SecretBroker['rotate'] = async (name) => {\n\t\tif (disposed) throw new Error('Broker is disposed');\n\t\tif (!options.adapter.rotate) {\n\t\t\tthrow new Error('Adapter does not support rotate()');\n\t\t}\n\t\tconst next = await options.adapter.rotate(name);\n\t\tconst now = clock();\n\t\tconst entry = cacheEntry(name, next, now);\n\t\tfireAudit({ at: now, event: 'rotate', fingerprint: entry.fingerprint, name });\n\t\treturn { fingerprint: entry.fingerprint, value: entry.value };\n\t};\n\n\tconst invalidate: SecretBroker['invalidate'] = (name) => {\n\t\tif (name === undefined) {\n\t\t\tcache.clear();\n\t\t} else {\n\t\t\tcache.delete(name);\n\t\t}\n\t\tfireAudit({ at: clock(), event: 'invalidate', name: name ?? null });\n\t};\n\n\tconst redact: SecretBroker['redact'] = (text) => {\n\t\tif (text.length === 0 || cache.size === 0) return text;\n\t\t// Replace longest values first so a substring of a longer secret\n\t\t// isn't blanked before its full match is found.\n\t\tconst ordered = Array.from(cache.entries())\n\t\t\t.filter(([, entry]) => entry.value.length >= minLen)\n\t\t\t.sort(([, a], [, b]) => b.value.length - a.value.length);\n\t\tlet out = text;\n\t\tfor (const [name, entry] of ordered) {\n\t\t\tif (!out.includes(entry.value)) continue;\n\t\t\tout = out.split(entry.value).join(`[REDACTED:${name}]`);\n\t\t}\n\t\treturn out;\n\t};\n\n\treturn {\n\t\tdispose: () => {\n\t\t\tdisposed = true;\n\t\t\tcache.clear();\n\t\t},\n\t\tfingerprint: fingerprintOf,\n\t\tinvalidate,\n\t\tredact,\n\t\tresolve,\n\t\trotate,\n\t};\n};\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "EE866E47E57910C764756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@absolutejs/secrets",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Host-side secret broker for multi-tenant Bun runtimes. Pluggable adapters (env-var, in-memory, composite); audit hook per resolve; safe fingerprints for logs; redact() walks known secrets out of arbitrary text before it lands in a log sink.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/absolutejs/secrets.git"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"license": "BSL-1.1",
|
|
14
|
+
"author": "Alex Kahn",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"absolutejs",
|
|
20
|
+
"bun",
|
|
21
|
+
"paas",
|
|
22
|
+
"multi-tenant",
|
|
23
|
+
"secrets",
|
|
24
|
+
"credential-broker",
|
|
25
|
+
"redaction",
|
|
26
|
+
"rotation"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "rm -rf dist && bun build src/index.ts --outdir dist --sourcemap --target=bun && tsc --project tsconfig.build.json",
|
|
30
|
+
"test": "bun test tests/",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"format": "prettier --write \"./**/*.{ts,json,md}\"",
|
|
33
|
+
"check:package": "bun run typecheck && bun run build && bun run test",
|
|
34
|
+
"release": "bun run format && bun run check:package && bun publish"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"bun-types": "^1.3.14"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/bun": "^1.3.14",
|
|
41
|
+
"prettier": "^3.8.3",
|
|
42
|
+
"typescript": "^6.0.3"
|
|
43
|
+
},
|
|
44
|
+
"exports": {
|
|
45
|
+
".": {
|
|
46
|
+
"types": "./dist/index.d.ts",
|
|
47
|
+
"import": "./dist/index.js",
|
|
48
|
+
"default": "./dist/index.js"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"files": ["dist", "README.md"]
|
|
52
|
+
}
|