credkeep 0.1.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.
@@ -0,0 +1,252 @@
1
+ /**
2
+ * What it takes to sign in to a site, by site name (`github`, or
3
+ * `google@ops` for a second account). Stored outside any repo or journal:
4
+ * a sealed 0600 JSON file on a laptop, env entries in a container. Read at
5
+ * the moment a login needs it, never carried on a plan or in a memo.
6
+ */
7
+ import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
8
+ import { homedir } from "node:os";
9
+ import { dirname } from "node:path";
10
+ import { z } from "zod";
11
+ import { isSealed, plainCipher } from "./cipher.js";
12
+ const expandHome = (p) => (p.startsWith("~/") ? `${homedir()}${p.slice(1)}` : p);
13
+ export const credentialSchema = z
14
+ .object({
15
+ username: z.string().min(1),
16
+ /** Absent when the account has none: it signs in through a provider (`via`). */
17
+ password: z.string().min(1).optional(),
18
+ /** The password before the last rotation: tried once when the site rejects the current one. */
19
+ previousPassword: z.string().min(1).optional(),
20
+ /** Base32 TOTP seed (the site's "manual entry key", spaces and dashes allowed); never a 6-digit code. */
21
+ totpSecret: z
22
+ .string()
23
+ .transform((s) => s.replace(/[\s=-]/g, "").toUpperCase())
24
+ .pipe(z
25
+ .string()
26
+ .regex(/^[A-Z2-7]{16,}$/, "totpSecret must be the base32 seed (16+ letters/digits), not a 6-digit code"))
27
+ .optional(),
28
+ /** One-time recovery codes the site handed out; used, then dropped. */
29
+ recoveryCodes: z.array(z.string().min(1)).default([]),
30
+ /** Where the site sends email codes; defaults to `username` when that is an address. */
31
+ codesInbox: z.string().email().optional(),
32
+ /** Passkeys enrolled by us (the virtual authenticator's export); loaded into the site's browser session. */
33
+ passkeys: z
34
+ .array(z.object({
35
+ rpId: z.string(),
36
+ credentialId: z.string(),
37
+ privateKey: z.string(),
38
+ userHandle: z.string().optional(),
39
+ signCount: z.number(),
40
+ isResidentCredential: z.boolean(),
41
+ }))
42
+ .default([]),
43
+ /** Sign in through this identity provider's button (`google`) instead of a password; the provider's own credential does the work. */
44
+ via: z.string().min(1).optional(),
45
+ /**
46
+ * A tripwire, not an account: nothing legitimate ever reads it. A `get`
47
+ * of a canary is an alarm (see canary.ts), and its password belongs on
48
+ * no host.
49
+ */
50
+ canary: z.boolean().optional(),
51
+ /** Where the sign-in page is, when the caller knows no login page of its own for the site. */
52
+ url: z.string().url().optional(),
53
+ /** When the account was made; a minted credential without it is a signup still owed. */
54
+ madeAt: z.string().datetime().optional(),
55
+ })
56
+ .refine((c) => c.password || c.via, { message: "a credential has a password or a via provider" });
57
+ const fileSchema = z.object({ sites: z.record(z.string(), credentialSchema) });
58
+ /**
59
+ * `{ "sites": { "<site>": Credential } }` at `path`, mode 0600, written
60
+ * atomically, sealed with `cipher` (a plain file written earlier is still
61
+ * read, and sealed on the next write).
62
+ */
63
+ export function fileCredentials(path, cipher = plainCipher) {
64
+ const file = expandHome(path);
65
+ // Opened and parsed once per version of the file: a `list` + `get` per name is one read, and
66
+ // a file another process rewrote (its mtime or size moved) is read again. A stat per call.
67
+ let cached = null;
68
+ const read = () => {
69
+ if (!existsSync(file)) {
70
+ cached = null;
71
+ return { sites: {} };
72
+ }
73
+ const st = statSync(file);
74
+ const stamp = `${st.mtimeMs}:${st.size}`;
75
+ if (cached?.stamp === stamp)
76
+ return cached.data;
77
+ const raw = readFileSync(file, "utf8");
78
+ const data = fileSchema.parse(JSON.parse(isSealed(raw) ? cipher.open(raw) : raw));
79
+ cached = { stamp, data };
80
+ return data;
81
+ };
82
+ return {
83
+ async get(site) {
84
+ return read().sites[site] ?? null;
85
+ },
86
+ async put(site, cred) {
87
+ const data = { sites: { ...read().sites, [site]: credentialSchema.parse(cred) } };
88
+ mkdirSync(dirname(file), { recursive: true, mode: 0o700 });
89
+ const tmp = `${file}.tmp`;
90
+ writeFileSync(tmp, cipher.seal(JSON.stringify(data, null, 2)), { mode: 0o600 });
91
+ renameSync(tmp, file);
92
+ cached = null;
93
+ },
94
+ async list() {
95
+ return Object.keys(read().sites);
96
+ },
97
+ };
98
+ }
99
+ export function memoryCredentials(init = {}) {
100
+ const sites = new Map(Object.entries(init).map(([k, v]) => [k, credentialSchema.parse(v)]));
101
+ return {
102
+ async get(site) {
103
+ return sites.get(site) ?? null;
104
+ },
105
+ async put(site, cred) {
106
+ sites.set(site, credentialSchema.parse(cred));
107
+ },
108
+ async list() {
109
+ return [...sites.keys()];
110
+ },
111
+ };
112
+ }
113
+ const DEFAULT_PREFIX = "CRED_";
114
+ /** `CRED_<SITE>_<FIELD>`: the env name a credential field travels under. */
115
+ export function credentialEnvName(site, field, o = {}) {
116
+ return `${o.prefix ?? DEFAULT_PREFIX}${envSiteName(site)}_${field}`;
117
+ }
118
+ /** `google@ops` → `GOOGLE__OPS`, `google-admin` → `GOOGLE_ADMIN`: the account keeps its own mark so `list` can read it back. */
119
+ const envSiteName = (site) => site
120
+ .replace(/@/g, "__")
121
+ .replace(/[^a-zA-Z0-9_]+/g, "_")
122
+ .toUpperCase();
123
+ const USERNAME = "_USERNAME";
124
+ const siteFromEnvName = (s) => s.toLowerCase().replace(/__/g, "@").replace(/_/g, "-");
125
+ /**
126
+ * A credential as env entries, for the store the box reads: username,
127
+ * password, TOTP seed, via provider, codes inbox. Recovery codes and
128
+ * passkeys stay on the machine that holds the file.
129
+ */
130
+ export function credentialEnv(site, cred, o = {}) {
131
+ const fields = [
132
+ ["USERNAME", cred.username],
133
+ ["PASSWORD", cred.password],
134
+ ["TOTP_SECRET", cred.totpSecret],
135
+ ["VIA", cred.via],
136
+ ["CODES_INBOX", cred.codesInbox],
137
+ ];
138
+ return fields
139
+ .filter((f) => Boolean(f[1]))
140
+ .map(([field, value]) => ({ name: credentialEnvName(site, field, o), value }));
141
+ }
142
+ /**
143
+ * `CRED_<SITE>_USERNAME` / `_PASSWORD` / `_TOTP_SECRET` / `_VIA` /
144
+ * `_CODES_INBOX`: the container form, where a Secret becomes env. Read-only.
145
+ */
146
+ export function envCredentials(env = process.env, o = {}) {
147
+ const prefix = o.prefix ?? DEFAULT_PREFIX;
148
+ const key = (site, field) => credentialEnvName(site, field, o);
149
+ return {
150
+ async get(site) {
151
+ const username = env[key(site, "USERNAME")];
152
+ const password = env[key(site, "PASSWORD")];
153
+ const via = env[key(site, "VIA")];
154
+ if (!username || !(password || via))
155
+ return null;
156
+ const totpSecret = env[key(site, "TOTP_SECRET")];
157
+ const codesInbox = env[key(site, "CODES_INBOX")];
158
+ return credentialSchema.parse({
159
+ username,
160
+ ...(password ? { password } : {}),
161
+ ...(totpSecret ? { totpSecret } : {}),
162
+ ...(via ? { via } : {}),
163
+ ...(codesInbox ? { codesInbox } : {}),
164
+ });
165
+ },
166
+ async put(site) {
167
+ throw new Error(`credentials for ${site}: env store is read-only; set ${key(site, "*")}`);
168
+ },
169
+ async list() {
170
+ return Object.keys(env)
171
+ .map((k) => k.startsWith(prefix) && k.endsWith(USERNAME)
172
+ ? k.slice(prefix.length, -USERNAME.length)
173
+ : undefined)
174
+ .filter((s) => Boolean(s))
175
+ .map(siteFromEnvName);
176
+ },
177
+ };
178
+ }
179
+ /**
180
+ * This machine's credentials into the env store, every site or the named
181
+ * ones: username, password, TOTP seed, via, codes inbox. Canaries never
182
+ * travel (a tripwire belongs to one machine); passkeys and recovery codes
183
+ * cannot. Answers what was pushed, never a value.
184
+ */
185
+ export async function pushCredentials(local, store, sites, o = {}) {
186
+ const chosen = sites?.length ? sites : await local.list();
187
+ const out = [];
188
+ for (const site of chosen) {
189
+ const cred = await local.get(site);
190
+ if (!cred)
191
+ throw new Error(`no credential stored here for ${site}`);
192
+ if (cred.canary)
193
+ continue;
194
+ const entries = credentialEnv(site, cred, o);
195
+ for (const e of entries)
196
+ await store.put(e.name, e.value);
197
+ out.push({ site, names: entries.map((e) => e.name) });
198
+ }
199
+ return out;
200
+ }
201
+ /**
202
+ * The env store's credentials into this machine's file, the other way: a
203
+ * second laptop, or a box's file for a passkey site. A site already here is
204
+ * kept unless `overwrite`, and even then its passkeys and recovery codes
205
+ * stay (env never carries them). Answers what was written and what was kept.
206
+ */
207
+ export async function pullCredentials(store, local, sites, o = {}) {
208
+ const env = Object.fromEntries((await store.all()).map((e) => [e.name, e.value]));
209
+ const remote = envCredentials(env, o);
210
+ const chosen = sites?.length ? sites : await remote.list();
211
+ const written = [];
212
+ const kept = [];
213
+ for (const site of chosen) {
214
+ const cred = await remote.get(site);
215
+ if (!cred)
216
+ throw new Error(`no credential in the shared store for ${site}: push it first`);
217
+ const here = await local.get(site);
218
+ if (here && !o.overwrite) {
219
+ kept.push(site);
220
+ continue;
221
+ }
222
+ await local.put(site, {
223
+ ...cred,
224
+ recoveryCodes: here?.recoveryCodes ?? cred.recoveryCodes,
225
+ passkeys: here?.passkeys ?? cred.passkeys,
226
+ });
227
+ written.push(site);
228
+ }
229
+ return { written, kept };
230
+ }
231
+ /** First store that has the site wins; writes go to `write`, which defaults to the last store. */
232
+ export function layeredCredentials(stores, write = stores.at(-1)) {
233
+ const first = write;
234
+ if (!first)
235
+ throw new Error("layeredCredentials needs at least one store");
236
+ return {
237
+ async get(site) {
238
+ for (const s of stores) {
239
+ const c = await s.get(site);
240
+ if (c)
241
+ return c;
242
+ }
243
+ return null;
244
+ },
245
+ put: (site, cred) => first.put(site, cred),
246
+ async list() {
247
+ const all = await Promise.all(stores.map((s) => s.list()));
248
+ return [...new Set(all.flat())];
249
+ },
250
+ };
251
+ }
252
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The one place a secret lives across machines: SSM Parameter Store, one
3
+ * SecureString per name under the app's path (`/myapp/config/NAME`). KMS
4
+ * at rest, IAM at the door, every read in CloudTrail. Values never enter
5
+ * argv, logs or errors.
6
+ */
7
+ import { type SSMClient } from "@aws-sdk/client-ssm";
8
+ export declare const ENV_KEY: RegExp;
9
+ export interface EnvEntry {
10
+ name: string;
11
+ value: string;
12
+ }
13
+ /** What the store knows about an entry besides its value. */
14
+ export interface EnvListing {
15
+ name: string;
16
+ /** When the value last changed. */
17
+ updatedAt: string | null;
18
+ /** When the value stops working (a token's expiry), if whoever stored it knew. */
19
+ expiresAt: string | null;
20
+ }
21
+ export interface PutOptions {
22
+ /** ISO time the value stops working; absent = it does not, or nobody knows. */
23
+ expiresAt?: string | undefined;
24
+ }
25
+ export interface EnvStore {
26
+ /** Names, change and expiry times; never values. */
27
+ list(): Promise<EnvListing[]>;
28
+ get(name: string): Promise<string | null>;
29
+ /** Every entry, decrypted: what a pull or a deploy reads. */
30
+ all(): Promise<EnvEntry[]>;
31
+ put(name: string, value: string, o?: PutOptions): Promise<void>;
32
+ remove(name: string): Promise<boolean>;
33
+ }
34
+ /**
35
+ * Entries that stop working within `withinMs` of `now` (already expired
36
+ * included), soonest first: what a watcher re-mints.
37
+ */
38
+ export declare function expiring(entries: readonly EnvListing[], withinMs: number, now?: number): EnvListing[];
39
+ /** SSM under `prefix`: `/prefix/NAME`. The client comes in so tests pass a fake. */
40
+ export declare function ssmEnvStore(ssm: SSMClient, prefix: string): EnvStore;
41
+ export declare function memoryEnvStore(initial?: Record<string, string>): EnvStore & {
42
+ values: Record<string, string>;
43
+ };
44
+ /**
45
+ * `KEY=VALUE` lines → entries. Comments, blanks and quotes as a shell would
46
+ * read them; a value naming a readable file whose content is JSON (a
47
+ * service account) is inlined so the store holds the secret, not a path.
48
+ */
49
+ export declare function parseDotenv(text: string, readFile?: (path: string) => string | null): EnvEntry[];
50
+ /** Entries → `KEY=VALUE` lines. A multi-line value cannot live in one line: `files` takes it and the line names the path. */
51
+ export declare function toDotenv(entries: EnvEntry[], files?: (name: string, value: string) => string): string;
52
+ /** Entries → `export KEY='…'` lines for `eval "$(…)"`; single quotes are the one thing escaped. */
53
+ export declare function toExports(entries: EnvEntry[]): string;
54
+ /** Upsert `entries` into an env file's text; other lines untouched. */
55
+ export declare function upsertDotenv(text: string, entries: EnvEntry[]): string;
@@ -0,0 +1,213 @@
1
+ /**
2
+ * The one place a secret lives across machines: SSM Parameter Store, one
3
+ * SecureString per name under the app's path (`/myapp/config/NAME`). KMS
4
+ * at rest, IAM at the door, every read in CloudTrail. Values never enter
5
+ * argv, logs or errors.
6
+ */
7
+ import { DeleteParameterCommand, DescribeParametersCommand, GetParameterCommand, GetParametersByPathCommand, ParameterNotFound, PutParameterCommand, } from "@aws-sdk/client-ssm";
8
+ /** SSM throttles writes at a few a second; a push of thirty keys backs off and goes on rather than dying. */
9
+ async function patient(call, sleep = (ms) => new Promise((r) => setTimeout(r, ms))) {
10
+ for (let attempt = 0;; attempt++) {
11
+ try {
12
+ return await call();
13
+ }
14
+ catch (err) {
15
+ const throttled = err instanceof Error &&
16
+ (err.name === "ThrottlingException" || /Rate exceeded/i.test(err.message));
17
+ if (!throttled || attempt >= 8)
18
+ throw err;
19
+ await sleep(200 * 2 ** attempt);
20
+ }
21
+ }
22
+ }
23
+ export const ENV_KEY = /^[A-Z][A-Z0-9_]*$/;
24
+ /**
25
+ * Entries that stop working within `withinMs` of `now` (already expired
26
+ * included), soonest first: what a watcher re-mints.
27
+ */
28
+ export function expiring(entries, withinMs, now = Date.now()) {
29
+ return entries
30
+ .filter((e) => e.expiresAt !== null && Date.parse(e.expiresAt) - now <= withinMs)
31
+ .sort((a, b) => (a.expiresAt ?? "").localeCompare(b.expiresAt ?? ""));
32
+ }
33
+ /** SSM keeps expiry in the parameter's description, which reads without decrypting. */
34
+ const EXPIRES = "expires ";
35
+ const describe = (o = {}) => o.expiresAt ? `${EXPIRES}${new Date(o.expiresAt).toISOString()}` : "no expiry";
36
+ const expiryOf = (description) => description?.startsWith(EXPIRES) ? description.slice(EXPIRES.length) : null;
37
+ /** SSM under `prefix`: `/prefix/NAME`. The client comes in so tests pass a fake. */
38
+ export function ssmEnvStore(ssm, prefix) {
39
+ const path = (name) => {
40
+ if (!ENV_KEY.test(name))
41
+ throw new Error(`env store: bad name ${name}`);
42
+ return `${prefix}/${name}`;
43
+ };
44
+ const nameOf = (p) => p?.slice(prefix.length + 1) ?? "";
45
+ const byName = (rows) => rows.sort((a, b) => a.name.localeCompare(b.name));
46
+ return {
47
+ async list() {
48
+ const out = [];
49
+ let next;
50
+ do {
51
+ const r = await patient(() => ssm.send(new DescribeParametersCommand({
52
+ ParameterFilters: [{ Key: "Path", Option: "OneLevel", Values: [prefix] }],
53
+ NextToken: next,
54
+ })));
55
+ for (const p of r.Parameters ?? [])
56
+ out.push({
57
+ name: nameOf(p.Name),
58
+ updatedAt: p.LastModifiedDate?.toISOString() ?? null,
59
+ expiresAt: expiryOf(p.Description),
60
+ });
61
+ next = r.NextToken;
62
+ } while (next);
63
+ return byName(out);
64
+ },
65
+ async all() {
66
+ const out = [];
67
+ let next;
68
+ do {
69
+ const r = await patient(() => ssm.send(new GetParametersByPathCommand({
70
+ Path: prefix,
71
+ Recursive: false,
72
+ WithDecryption: true,
73
+ NextToken: next,
74
+ })));
75
+ for (const p of r.Parameters ?? [])
76
+ out.push({ name: nameOf(p.Name), value: p.Value ?? "" });
77
+ next = r.NextToken;
78
+ } while (next);
79
+ return byName(out);
80
+ },
81
+ async get(name) {
82
+ try {
83
+ const r = await ssm.send(new GetParameterCommand({ Name: path(name), WithDecryption: true }));
84
+ return r.Parameter?.Value ?? null;
85
+ }
86
+ catch (err) {
87
+ if (err instanceof ParameterNotFound)
88
+ return null;
89
+ throw err;
90
+ }
91
+ },
92
+ async put(name, value, o) {
93
+ if (!value)
94
+ throw new Error(`env store: ${name} is empty`);
95
+ await patient(() => ssm.send(new PutParameterCommand({
96
+ Name: path(name),
97
+ Value: value,
98
+ // Always said, so a re-mint without an expiry clears the old one.
99
+ Description: describe(o),
100
+ Type: "SecureString",
101
+ // Past 4 KB (a service-account JSON) SSM needs the advanced tier; this picks it only then.
102
+ Tier: "Intelligent-Tiering",
103
+ Overwrite: true,
104
+ })));
105
+ },
106
+ async remove(name) {
107
+ try {
108
+ await ssm.send(new DeleteParameterCommand({ Name: path(name) }));
109
+ return true;
110
+ }
111
+ catch (err) {
112
+ if (err instanceof ParameterNotFound)
113
+ return false;
114
+ throw err;
115
+ }
116
+ },
117
+ };
118
+ }
119
+ export function memoryEnvStore(initial = {}) {
120
+ const values = { ...initial };
121
+ const expires = new Map();
122
+ return {
123
+ values,
124
+ list: async () => Object.keys(values)
125
+ .sort()
126
+ .map((name) => ({ name, updatedAt: null, expiresAt: expiryOf(expires.get(name)) })),
127
+ all: async () => Object.entries(values)
128
+ .sort(([a], [b]) => a.localeCompare(b))
129
+ .map(([name, value]) => ({ name, value })),
130
+ get: async (name) => values[name] ?? null,
131
+ async put(name, value, o) {
132
+ if (!ENV_KEY.test(name))
133
+ throw new Error(`env store: bad name ${name}`);
134
+ values[name] = value;
135
+ expires.set(name, describe(o));
136
+ },
137
+ async remove(name) {
138
+ const had = name in values;
139
+ delete values[name];
140
+ expires.delete(name);
141
+ return had;
142
+ },
143
+ };
144
+ }
145
+ /**
146
+ * `KEY=VALUE` lines → entries. Comments, blanks and quotes as a shell would
147
+ * read them; a value naming a readable file whose content is JSON (a
148
+ * service account) is inlined so the store holds the secret, not a path.
149
+ */
150
+ export function parseDotenv(text, readFile) {
151
+ const out = [];
152
+ for (const raw of text.split("\n")) {
153
+ const line = raw.trim();
154
+ if (!line || line.startsWith("#") || !line.includes("="))
155
+ continue;
156
+ const i = line.indexOf("=");
157
+ const name = line
158
+ .slice(0, i)
159
+ .trim()
160
+ .replace(/^export\s+/, "");
161
+ let value = line.slice(i + 1).trim();
162
+ const q = value[0];
163
+ if (q === '"' || q === "'") {
164
+ const end = value.indexOf(q, 1);
165
+ value = end > 0 ? value.slice(1, end) : value.slice(1);
166
+ }
167
+ else
168
+ value = value.replace(/\s+#.*$/, "");
169
+ if (!ENV_KEY.test(name) || !value)
170
+ continue;
171
+ if (readFile && !value.trimStart().startsWith("{") && /\.json$/.test(value)) {
172
+ const inlined = readFile(value);
173
+ if (inlined?.trimStart().startsWith("{"))
174
+ value = inlined;
175
+ }
176
+ out.push({ name, value });
177
+ }
178
+ return out;
179
+ }
180
+ /** Entries → `KEY=VALUE` lines. A multi-line value cannot live in one line: `files` takes it and the line names the path. */
181
+ export function toDotenv(entries, files) {
182
+ return `${entries
183
+ .map(({ name, value }) => {
184
+ if (!/[\r\n]/.test(value))
185
+ return `${name}=${value}`;
186
+ if (!files)
187
+ throw new Error(`${name} spans lines; give it a file`);
188
+ return `${name}=${files(name, value)}`;
189
+ })
190
+ .join("\n")}\n`;
191
+ }
192
+ /** Entries → `export KEY='…'` lines for `eval "$(…)"`; single quotes are the one thing escaped. */
193
+ export function toExports(entries) {
194
+ return `${entries
195
+ .map(({ name, value }) => `export ${name}='${value.replace(/'/g, `'\\''`)}'`)
196
+ .join("\n")}\n`;
197
+ }
198
+ /** Upsert `entries` into an env file's text; other lines untouched. */
199
+ export function upsertDotenv(text, entries) {
200
+ const lines = text.split("\n");
201
+ if (lines.at(-1) === "")
202
+ lines.pop();
203
+ for (const { name, value } of entries) {
204
+ const line = `${name}=${value}`;
205
+ const i = lines.findIndex((l) => l.startsWith(`${name}=`));
206
+ if (i >= 0)
207
+ lines[i] = line;
208
+ else
209
+ lines.push(line);
210
+ }
211
+ return `${lines.join("\n")}\n`;
212
+ }
213
+ //# sourceMappingURL=env-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-store.js","sourceRoot":"","sources":["../src/env-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export { fileAudit, memoryAudit, type SecretAudit, type SecretUse } from "./audit.js";
2
+ export { type CanaryOptions, CanaryTripped, canaryCredential, canaryStore, isCanary, } from "./canary.js";
3
+ export { type Chained, type ChainedFile, chainedFile, rowHash, type Verification, verifyChain, } from "./chain.js";
4
+ export { aesGcmCipher, type Cipher, isSealed, type KeychainItem, keychainKey, plainCipher, trustKeychainKey, } from "./cipher.js";
5
+ export { type Credential, type CredentialEnvStore, type CredentialInput, type CredentialStore, credentialEnv, credentialEnvName, credentialSchema, type EnvNaming, envCredentials, fileCredentials, layeredCredentials, memoryCredentials, pullCredentials, pushCredentials, } from "./credentials.js";
6
+ export { ENV_KEY, type EnvEntry, type EnvListing, type EnvStore, expiring, memoryEnvStore, type PutOptions, parseDotenv, ssmEnvStore, toDotenv, toExports, upsertDotenv, } from "./env-store.js";
7
+ export { newPassword } from "./passwords.js";
8
+ export { envSecrets, memorySecrets, type SecretSource, type TrackingSecrets, trackingSecrets, } from "./secrets.js";
9
+ export { tailJson, tailLines } from "./tail.js";
10
+ export { base32Decode, findTotpSecret, parseOtpauth, type TotpAlgorithm, type TotpOptions, type TotpParams, totp, totpRemainingMs, } from "./totp.js";
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export { fileAudit, memoryAudit } from "./audit.js";
2
+ export { CanaryTripped, canaryCredential, canaryStore, isCanary, } from "./canary.js";
3
+ export { chainedFile, rowHash, verifyChain, } from "./chain.js";
4
+ export { aesGcmCipher, isSealed, keychainKey, plainCipher, trustKeychainKey, } from "./cipher.js";
5
+ export { credentialEnv, credentialEnvName, credentialSchema, envCredentials, fileCredentials, layeredCredentials, memoryCredentials, pullCredentials, pushCredentials, } from "./credentials.js";
6
+ export { ENV_KEY, expiring, memoryEnvStore, parseDotenv, ssmEnvStore, toDotenv, toExports, upsertDotenv, } from "./env-store.js";
7
+ export { newPassword } from "./passwords.js";
8
+ export { envSecrets, memorySecrets, trackingSecrets, } from "./secrets.js";
9
+ export { tailJson, tailLines } from "./tail.js";
10
+ export { base32Decode, findTotpSecret, parseOtpauth, totp, totpRemainingMs, } from "./totp.js";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAoC,MAAM,YAAY,CAAC;AACtF,OAAO,EAEL,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EAGL,WAAW,EACX,OAAO,EAEP,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EAEZ,QAAQ,EAER,WAAW,EACX,WAAW,EACX,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAKL,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAEhB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,OAAO,EAIP,QAAQ,EACR,cAAc,EAEd,WAAW,EACX,WAAW,EACX,QAAQ,EACR,SAAS,EACT,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EACL,UAAU,EACV,aAAa,EAGb,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EACL,YAAY,EACZ,cAAc,EACd,YAAY,EAIZ,IAAI,EACJ,eAAe,GAChB,MAAM,WAAW,CAAC"}
@@ -0,0 +1,2 @@
1
+ /** A password no site refuses: 24 chars, at least one of each class, no look-alikes (l/1/O/0). */
2
+ export declare function newPassword(length?: number, pick?: (n: number) => number): string;
@@ -0,0 +1,19 @@
1
+ /** Passwords drawn here: nobody picks them, nobody sees them. */
2
+ import { randomInt } from "node:crypto";
3
+ const LOWER = "abcdefghijkmnopqrstuvwxyz";
4
+ const UPPER = "ABCDEFGHJKLMNPQRSTUVWXYZ";
5
+ const DIGIT = "23456789";
6
+ const SYMBOL = "!@#$%^&*-_=+";
7
+ const ALL = LOWER + UPPER + DIGIT + SYMBOL;
8
+ /** A password no site refuses: 24 chars, at least one of each class, no look-alikes (l/1/O/0). */
9
+ export function newPassword(length = 24, pick = randomInt) {
10
+ const chars = [LOWER, UPPER, DIGIT, SYMBOL].map((set) => set[pick(set.length)] ?? "a");
11
+ while (chars.length < length)
12
+ chars.push(ALL[pick(ALL.length)] ?? "a");
13
+ for (let i = chars.length - 1; i > 0; i--) {
14
+ const j = pick(i + 1);
15
+ [chars[i], chars[j]] = [chars[j], chars[i]];
16
+ }
17
+ return chars.join("");
18
+ }
19
+ //# sourceMappingURL=passwords.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passwords.js","sourceRoot":"","sources":["../src/passwords.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,KAAK,GAAG,2BAA2B,CAAC;AAC1C,MAAM,KAAK,GAAG,0BAA0B,CAAC;AACzC,MAAM,KAAK,GAAG,UAAU,CAAC;AACzB,MAAM,MAAM,GAAG,cAAc,CAAC;AAC9B,MAAM,GAAG,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAE3C,kGAAkG;AAClG,MAAM,UAAU,WAAW,CAAC,MAAM,GAAG,EAAE,EAAE,OAA8B,SAAS;IAC9E,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;IACvF,OAAO,KAAK,CAAC,MAAM,GAAG,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;IACvE,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACtB,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAW,EAAE,KAAK,CAAC,CAAC,CAAW,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * One named value, asked for by key at the moment it is needed (a card
3
+ * number, an API key), never carried on a plan or in a log. Env is the
4
+ * default source; the shared store (env-store.ts) is another.
5
+ */
6
+ export interface SecretSource {
7
+ get(key: string): Promise<string>;
8
+ }
9
+ /** A key in camelCase (`cardCvv`) → `SECRET_CARD_CVV` in the environment, under the app's prefix. */
10
+ export declare function envSecrets(env?: NodeJS.ProcessEnv, prefix?: string): SecretSource;
11
+ export declare function memorySecrets(values: Record<string, string>): SecretSource;
12
+ /** A secret source that remembers what it handed out, so a caller can tell a secret's value from any other. */
13
+ export interface TrackingSecrets extends SecretSource {
14
+ /** The key a value was handed out under, or null. */
15
+ keyOf(value: string): string | null;
16
+ }
17
+ export declare function trackingSecrets(source: SecretSource): TrackingSecrets;