@tillstack/secrets-node 0.3.0 → 0.4.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 +109 -0
- package/dist/index.d.mts +15 -7
- package/dist/index.d.ts +15 -7
- package/dist/index.js +61 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +59 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# @tillstack/secrets-node
|
|
2
|
+
|
|
3
|
+
[TillSecrets](https://tilldev.dev/secrets) for Node.js. Pull your environment's encrypted config at boot and load it into `process.env`, dotenv-style, without committing a `.env`.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @tillstack/secrets-node
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Load at boot
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { load } from '@tillstack/secrets-node'
|
|
15
|
+
|
|
16
|
+
await load() // existing env vars win unless you pass { override: true }
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`load()` throws if the token was refused any restricted (tier 7+) or sealed key, rather than booting without it. Use a token pinned to the environment, or pass `{ allowPartial: true }` to continue without those keys.
|
|
20
|
+
|
|
21
|
+
Environment:
|
|
22
|
+
|
|
23
|
+
| Variable | |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `TILLSECRETS_TOKEN` | Service token (`ts_…`). Required unless you use a workload identity. |
|
|
26
|
+
| `TILLSECRETS_IDENTITY` | A workload identity id, instead of a token. Ignored when `TILLSECRETS_TOKEN` is set. |
|
|
27
|
+
| `TILLSECRETS_ID_TOKEN` / `TILLSECRETS_ID_TOKEN_FILE` | An OIDC token for the identity, where the platform doesn't provide one itself. |
|
|
28
|
+
| `TILLSECRETS_ENV` | Environment id, only for project-scoped tokens. |
|
|
29
|
+
| `TILLSECRETS_URL` | API base. Default `https://tilldev.dev`. |
|
|
30
|
+
| `TILLSECRETS_LEASE_URL` | Dynamic-lease service. Default `https://secrets.tilldev.dev`. |
|
|
31
|
+
| `TILLSECRETS_HOST_KEY_FILE` | This host's key file, for a token bound to a host. Default `~/.tilldev/host-key` when it exists. |
|
|
32
|
+
| `TILLSECRETS_HOST_KEY` | The key file's contents, where there is no disk. |
|
|
33
|
+
|
|
34
|
+
Tokens go only over https (plain http is allowed for localhost). Answers come back encrypted to a post-quantum key (X-Wing) inside TLS: this host's key, or a fresh one per call. A token bound to a host with `tilldev secrets host-key init` and `tilldev secrets tokens create --host-key` answers only to that key, so a copy of it elsewhere gets nothing. A key file other users can read is refused.
|
|
35
|
+
|
|
36
|
+
## Client
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { createClient } from '@tillstack/secrets-node'
|
|
40
|
+
|
|
41
|
+
const secrets = createClient()
|
|
42
|
+
const { secrets: values, dynamic } = await secrets.pull()
|
|
43
|
+
|
|
44
|
+
// Short-lived database credentials, minted on demand (read_write token).
|
|
45
|
+
const lease = await secrets.lease({ secretKey: 'REPORTING_DB', ttlSeconds: 900 })
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Workload identity
|
|
49
|
+
|
|
50
|
+
On a CI run, cloud workload or server with a platform identity, set `TILLSECRETS_IDENTITY` instead of storing a token:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { load, createClient } from '@tillstack/secrets-node'
|
|
54
|
+
|
|
55
|
+
await load() // reads TILLSECRETS_IDENTITY
|
|
56
|
+
|
|
57
|
+
const client = createClient({ identity: '3f1c…' })
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The SDK finds the platform's proof on its own:
|
|
61
|
+
|
|
62
|
+
- GitHub Actions: the job's OIDC token;
|
|
63
|
+
- GitLab CI: the token in `TILLSECRETS_ID_TOKEN`;
|
|
64
|
+
- Bitbucket Pipelines, Semaphore, HCP Terraform, Spacelift and Modal: the token the platform puts in the environment or the workspace;
|
|
65
|
+
- CircleCI and Buildkite: a token from `circleci run oidc get` or `buildkite-agent oidc request-token`;
|
|
66
|
+
- Google Cloud: the metadata server;
|
|
67
|
+
- Azure: the managed identity endpoint (App Service, Functions, Container Apps) or instance metadata (VMs);
|
|
68
|
+
- Kubernetes: a projected token at `/var/run/secrets/tilldev/token`;
|
|
69
|
+
- Fly.io: the Machines API;
|
|
70
|
+
- AWS: credentials from the environment, an EKS service account role, ECS or EC2;
|
|
71
|
+
- a server with no platform identity: its host key.
|
|
72
|
+
|
|
73
|
+
Vercel and Deno Deploy hand out tokens only through their own libraries, so pass one in:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { getVercelOidcToken } from '@vercel/oidc'
|
|
77
|
+
import { createClient } from '@tillstack/secrets-node'
|
|
78
|
+
|
|
79
|
+
const secrets = createClient({
|
|
80
|
+
identity: '3f1c…',
|
|
81
|
+
workload: { idToken: (audience) => getVercelOidcToken({ audience }) },
|
|
82
|
+
})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
It trades that proof for a token that lasts an hour by default, and renews it before it expires. `TILLSECRETS_ID_TOKEN` or `TILLSECRETS_ID_TOKEN_FILE` overrides where the token comes from. Create identities with `tilldev secrets identities create`. See https://tilldev.dev/docs/tillsecrets/workload-identity.
|
|
86
|
+
|
|
87
|
+
## Broker
|
|
88
|
+
|
|
89
|
+
Let an agent *use* a secret without ever seeing it. It sends `secretref://` handles, and the broker substitutes the real value only into the outgoing request:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { createBroker, secretref } from '@tillstack/secrets-node'
|
|
93
|
+
|
|
94
|
+
const broker = createBroker()
|
|
95
|
+
const res = await broker.fetch('https://api.stripe.com/v1/charges', {
|
|
96
|
+
method: 'POST',
|
|
97
|
+
headers: { authorization: `Bearer ${secretref('shop', 'prod', 'STRIPE_KEY')}` },
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
When running under the TillForge bridge, `createBroker()` picks up `TILLDEV_BROKER_URL` and `TILLDEV_BROKER_TOKEN`, so the secret is spent on your machine. Otherwise it goes through the cloud broker and needs `TILLDEV_TOKEN` and `TILLDEV_AGENT_KEY`. An agent should use its own [agent token](https://tilldev.dev/docs/tillsecrets/agents) instead: set `TILLDEV_AGENT_TOKEN` (a `ta_…` token) and leave `TILLDEV_AGENT_KEY` unset; the token names the agent.
|
|
102
|
+
|
|
103
|
+
## Docs
|
|
104
|
+
|
|
105
|
+
https://tilldev.dev/docs/tillsecrets/quickstart
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
Apache-2.0
|
package/dist/index.d.mts
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
|
-
import { SecretsClientOptions, BrokerClient, SecretsClient, PullResult } from '@tillstack/secrets-core';
|
|
1
|
+
import { SecretsClientOptions, BrokerClient, SecretsClient, PullResult, WorkloadEnvironment } from '@tillstack/secrets-core';
|
|
2
2
|
export * from '@tillstack/secrets-core';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/** The host key file made by `tilldev secrets host-key init`. */
|
|
5
|
+
declare const DEFAULT_HOST_KEY_FILE: string;
|
|
6
|
+
/** `hostKey`, else `TILLSECRETS_HOST_KEY_FILE`, `TILLSECRETS_HOST_KEY`, then ~/.tilldev/host-key when it exists. */
|
|
7
|
+
declare function resolveHostKey(opts?: Partial<SecretsClientOptions>, env?: NodeJS.ProcessEnv, defaultFile?: string): string | Uint8Array | undefined;
|
|
8
|
+
/** Where a workload identity's proof comes from on Node: the environment, token files, the Fly Machines socket, and platform CLIs. */
|
|
9
|
+
declare function nodeWorkload(env?: NodeJS.ProcessEnv): Omit<WorkloadEnvironment, 'fetch' | 'timeoutMs'>;
|
|
10
|
+
/** Create a client from options or the environment (`TILLSECRETS_TOKEN` or `TILLSECRETS_IDENTITY`,
|
|
11
|
+
* optional `TILLSECRETS_URL`, `TILLSECRETS_ENV`, and the host key as in `resolveHostKey`). */
|
|
6
12
|
declare function createClient(opts?: Partial<SecretsClientOptions>): SecretsClient;
|
|
7
13
|
interface LoadOptions extends Partial<SecretsClientOptions> {
|
|
8
14
|
/** Overwrite keys already present in process.env (default false — existing
|
|
9
15
|
* wins, matching dotenv). */
|
|
10
16
|
override?: boolean;
|
|
17
|
+
/** Inject what was returned even if restricted or sealed keys were withheld (default false — throw). */
|
|
18
|
+
allowPartial?: boolean;
|
|
11
19
|
}
|
|
12
20
|
/** Pull secrets and inject them into process.env. Returns the pulled map. */
|
|
13
21
|
declare function load(opts?: LoadOptions): Promise<PullResult>;
|
|
@@ -19,10 +27,10 @@ interface CreateBrokerOptions {
|
|
|
19
27
|
/** Cloud API base (derives the spend endpoint). Ignored in bridge mode.
|
|
20
28
|
* Defaults to `TILLDEV_URL` / `TILLSECRETS_URL` / `https://tilldev.dev`. */
|
|
21
29
|
url?: string;
|
|
22
|
-
/** Bearer token
|
|
23
|
-
* bridge run token (`TILLDEV_BROKER_TOKEN`, injected by the daemon). */
|
|
30
|
+
/** Bearer token: an agent token (`ta_`, `TILLDEV_AGENT_TOKEN`), a session / `tp_` API key
|
|
31
|
+
* (`TILLDEV_TOKEN`), or the bridge run token (`TILLDEV_BROKER_TOKEN`, injected by the daemon). */
|
|
24
32
|
token?: string;
|
|
25
|
-
/** Stable agent identity. Required in cloud mode
|
|
33
|
+
/** Stable agent identity. Required in cloud mode unless the token is a `ta_` agent token, which names itself. */
|
|
26
34
|
agentKey?: string;
|
|
27
35
|
/** Session id for session-scoped grants (or `TILLDEV_SESSION`). */
|
|
28
36
|
sessionId?: string;
|
|
@@ -46,4 +54,4 @@ interface CreateBrokerOptions {
|
|
|
46
54
|
*/
|
|
47
55
|
declare function createBroker(opts?: CreateBrokerOptions): BrokerClient;
|
|
48
56
|
|
|
49
|
-
export { type CreateBrokerOptions, type LoadOptions, createBroker, createClient, load };
|
|
57
|
+
export { type CreateBrokerOptions, DEFAULT_HOST_KEY_FILE, type LoadOptions, createBroker, createClient, load, nodeWorkload, resolveHostKey };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
|
-
import { SecretsClientOptions, BrokerClient, SecretsClient, PullResult } from '@tillstack/secrets-core';
|
|
1
|
+
import { SecretsClientOptions, BrokerClient, SecretsClient, PullResult, WorkloadEnvironment } from '@tillstack/secrets-core';
|
|
2
2
|
export * from '@tillstack/secrets-core';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/** The host key file made by `tilldev secrets host-key init`. */
|
|
5
|
+
declare const DEFAULT_HOST_KEY_FILE: string;
|
|
6
|
+
/** `hostKey`, else `TILLSECRETS_HOST_KEY_FILE`, `TILLSECRETS_HOST_KEY`, then ~/.tilldev/host-key when it exists. */
|
|
7
|
+
declare function resolveHostKey(opts?: Partial<SecretsClientOptions>, env?: NodeJS.ProcessEnv, defaultFile?: string): string | Uint8Array | undefined;
|
|
8
|
+
/** Where a workload identity's proof comes from on Node: the environment, token files, the Fly Machines socket, and platform CLIs. */
|
|
9
|
+
declare function nodeWorkload(env?: NodeJS.ProcessEnv): Omit<WorkloadEnvironment, 'fetch' | 'timeoutMs'>;
|
|
10
|
+
/** Create a client from options or the environment (`TILLSECRETS_TOKEN` or `TILLSECRETS_IDENTITY`,
|
|
11
|
+
* optional `TILLSECRETS_URL`, `TILLSECRETS_ENV`, and the host key as in `resolveHostKey`). */
|
|
6
12
|
declare function createClient(opts?: Partial<SecretsClientOptions>): SecretsClient;
|
|
7
13
|
interface LoadOptions extends Partial<SecretsClientOptions> {
|
|
8
14
|
/** Overwrite keys already present in process.env (default false — existing
|
|
9
15
|
* wins, matching dotenv). */
|
|
10
16
|
override?: boolean;
|
|
17
|
+
/** Inject what was returned even if restricted or sealed keys were withheld (default false — throw). */
|
|
18
|
+
allowPartial?: boolean;
|
|
11
19
|
}
|
|
12
20
|
/** Pull secrets and inject them into process.env. Returns the pulled map. */
|
|
13
21
|
declare function load(opts?: LoadOptions): Promise<PullResult>;
|
|
@@ -19,10 +27,10 @@ interface CreateBrokerOptions {
|
|
|
19
27
|
/** Cloud API base (derives the spend endpoint). Ignored in bridge mode.
|
|
20
28
|
* Defaults to `TILLDEV_URL` / `TILLSECRETS_URL` / `https://tilldev.dev`. */
|
|
21
29
|
url?: string;
|
|
22
|
-
/** Bearer token
|
|
23
|
-
* bridge run token (`TILLDEV_BROKER_TOKEN`, injected by the daemon). */
|
|
30
|
+
/** Bearer token: an agent token (`ta_`, `TILLDEV_AGENT_TOKEN`), a session / `tp_` API key
|
|
31
|
+
* (`TILLDEV_TOKEN`), or the bridge run token (`TILLDEV_BROKER_TOKEN`, injected by the daemon). */
|
|
24
32
|
token?: string;
|
|
25
|
-
/** Stable agent identity. Required in cloud mode
|
|
33
|
+
/** Stable agent identity. Required in cloud mode unless the token is a `ta_` agent token, which names itself. */
|
|
26
34
|
agentKey?: string;
|
|
27
35
|
/** Session id for session-scoped grants (or `TILLDEV_SESSION`). */
|
|
28
36
|
sessionId?: string;
|
|
@@ -46,4 +54,4 @@ interface CreateBrokerOptions {
|
|
|
46
54
|
*/
|
|
47
55
|
declare function createBroker(opts?: CreateBrokerOptions): BrokerClient;
|
|
48
56
|
|
|
49
|
-
export { type CreateBrokerOptions, type LoadOptions, createBroker, createClient, load };
|
|
57
|
+
export { type CreateBrokerOptions, DEFAULT_HOST_KEY_FILE, type LoadOptions, createBroker, createClient, load, nodeWorkload, resolveHostKey };
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,71 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var child_process = require('child_process');
|
|
4
|
+
var fs = require('fs');
|
|
5
|
+
var promises = require('fs/promises');
|
|
6
|
+
var http = require('http');
|
|
7
|
+
var os = require('os');
|
|
8
|
+
var path = require('path');
|
|
3
9
|
var secretsCore = require('@tillstack/secrets-core');
|
|
4
10
|
|
|
5
11
|
// src/index.ts
|
|
12
|
+
var DEFAULT_HOST_KEY_FILE = path.join(os.homedir(), ".tilldev", "host-key");
|
|
13
|
+
function readHostKeyFile(file) {
|
|
14
|
+
if (process.platform !== "win32" && fs.statSync(file).mode & 63) {
|
|
15
|
+
throw new secretsCore.SecretsError(`TillSecrets: host key ${file} is readable by other users; run: chmod 600 ${file}`);
|
|
16
|
+
}
|
|
17
|
+
return fs.readFileSync(file, "utf8").trim();
|
|
18
|
+
}
|
|
19
|
+
function resolveHostKey(opts, env = process.env, defaultFile = DEFAULT_HOST_KEY_FILE) {
|
|
20
|
+
if (opts?.hostKey !== void 0 || opts?.seal === false) return opts?.hostKey;
|
|
21
|
+
if (env.TILLSECRETS_HOST_KEY_FILE) return readHostKeyFile(env.TILLSECRETS_HOST_KEY_FILE);
|
|
22
|
+
if (env.TILLSECRETS_HOST_KEY) return env.TILLSECRETS_HOST_KEY;
|
|
23
|
+
if (fs.existsSync(defaultFile)) return readHostKeyFile(defaultFile);
|
|
24
|
+
return void 0;
|
|
25
|
+
}
|
|
26
|
+
function unixPost(socket, path, body) {
|
|
27
|
+
return new Promise((ok, fail) => {
|
|
28
|
+
const req = http.request({ socketPath: socket, path, method: "POST", headers: { "content-type": "application/json" }, timeout: 3e3 }, (res) => {
|
|
29
|
+
let out = "";
|
|
30
|
+
res.setEncoding("utf8");
|
|
31
|
+
res.on("data", (d) => out += d);
|
|
32
|
+
res.on("end", () => (res.statusCode ?? 0) < 300 ? ok(out) : fail(new Error(`${res.statusCode}`)));
|
|
33
|
+
});
|
|
34
|
+
req.on("timeout", () => req.destroy(new Error("timeout")));
|
|
35
|
+
req.on("error", fail);
|
|
36
|
+
req.end(body);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function run(command, args) {
|
|
40
|
+
return new Promise((ok, fail) => child_process.execFile(command, args, { timeout: 1e4, maxBuffer: 64 * 1024 }, (e, out) => e ? fail(e) : ok(out)));
|
|
41
|
+
}
|
|
42
|
+
function nodeWorkload(env = process.env) {
|
|
43
|
+
return { env, readFile: (p) => promises.readFile(p, "utf8"), unixPost, run };
|
|
44
|
+
}
|
|
6
45
|
function createClient(opts) {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
46
|
+
const identity = opts?.identity ?? (opts?.token ? void 0 : process.env.TILLSECRETS_IDENTITY || void 0);
|
|
47
|
+
const token = identity ? void 0 : opts?.token ?? process.env.TILLSECRETS_TOKEN;
|
|
48
|
+
if (!token && !identity) throw new Error("TillSecrets: no token \u2014 pass one or set TILLSECRETS_TOKEN (or TILLSECRETS_IDENTITY for a workload identity)");
|
|
49
|
+
const built = identity ? { ...opts, identity, workload: { ...nodeWorkload(), ...opts?.workload } } : { ...opts, token };
|
|
10
50
|
const url = opts?.url ?? process.env.TILLSECRETS_URL;
|
|
11
51
|
if (url) built.url = url;
|
|
52
|
+
const leaseUrl = opts?.leaseUrl ?? process.env.TILLSECRETS_LEASE_URL;
|
|
53
|
+
if (leaseUrl) built.leaseUrl = leaseUrl;
|
|
12
54
|
const env = opts?.environmentId ?? process.env.TILLSECRETS_ENV;
|
|
13
55
|
if (env) built.environmentId = env;
|
|
14
|
-
|
|
15
|
-
if (
|
|
56
|
+
const hostKey = resolveHostKey(opts);
|
|
57
|
+
if (hostKey !== void 0) built.hostKey = hostKey;
|
|
16
58
|
return new secretsCore.SecretsClient(built);
|
|
17
59
|
}
|
|
18
60
|
async function load(opts = {}) {
|
|
19
61
|
const client = createClient(opts);
|
|
20
62
|
const result = await client.pull();
|
|
63
|
+
if (result.withheld.length && !opts.allowPartial) {
|
|
64
|
+
throw new secretsCore.SecretsError(
|
|
65
|
+
`TillSecrets: withheld restricted or sealed key(s) ${result.withheld.join(", ")} \u2014 they need a token pinned to this environment (or pass allowPartial)`,
|
|
66
|
+
403
|
|
67
|
+
);
|
|
68
|
+
}
|
|
21
69
|
for (const [k, v] of Object.entries(result.secrets)) {
|
|
22
70
|
if (opts.override || process.env[k] === void 0) process.env[k] = v;
|
|
23
71
|
}
|
|
@@ -43,17 +91,22 @@ function createBroker(opts = {}) {
|
|
|
43
91
|
return new secretsCore.BrokerClient(built);
|
|
44
92
|
}
|
|
45
93
|
const base = (opts.url ?? process.env.TILLDEV_URL ?? process.env.TILLSECRETS_URL ?? "https://tilldev.dev").replace(/\/$/, "");
|
|
46
|
-
const token = opts.token
|
|
47
|
-
if (!token) throw new secretsCore.SecretsError("TillSecrets broker: no token \u2014 pass one or set
|
|
48
|
-
if (!built.agentKey
|
|
94
|
+
const token = opts.token || process.env.TILLDEV_AGENT_TOKEN || process.env.TILLDEV_TOKEN || process.env.TILLSECRETS_BROKER_TOKEN;
|
|
95
|
+
if (!token) throw new secretsCore.SecretsError("TillSecrets broker: no token \u2014 pass one or set TILLDEV_AGENT_TOKEN");
|
|
96
|
+
if (!built.agentKey && !token.startsWith("ta_")) {
|
|
97
|
+
throw new secretsCore.SecretsError("TillSecrets broker: cloud mode needs an agentKey (or TILLDEV_AGENT_KEY), or a ta_ agent token");
|
|
98
|
+
}
|
|
49
99
|
built.endpoint = `${base}/api/secrets/broker/spend`;
|
|
50
100
|
built.token = token;
|
|
51
101
|
return new secretsCore.BrokerClient(built);
|
|
52
102
|
}
|
|
53
103
|
|
|
104
|
+
exports.DEFAULT_HOST_KEY_FILE = DEFAULT_HOST_KEY_FILE;
|
|
54
105
|
exports.createBroker = createBroker;
|
|
55
106
|
exports.createClient = createClient;
|
|
56
107
|
exports.load = load;
|
|
108
|
+
exports.nodeWorkload = nodeWorkload;
|
|
109
|
+
exports.resolveHostKey = resolveHostKey;
|
|
57
110
|
Object.keys(secretsCore).forEach(function (k) {
|
|
58
111
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
59
112
|
enumerable: true,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["SecretsClient","SecretsError","BrokerClient"],"mappings":"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","file":"index.js","sourcesContent":["// @tillstack/secrets-node — TillSecrets client for Node.js.\n//\n// import { load } from '@tillstack/secrets-node'\n// await load() // reads TILLSECRETS_TOKEN, injects into process.env\n// // ... process.env.STRIPE_KEY is now populated\n//\n// or explicitly:\n// const client = createClient({ token })\n// const { secrets } = await client.pull()\n\nimport {\n SecretsClient,\n SecretsError,\n BrokerClient,\n type SecretsClientOptions,\n type PullResult,\n type BrokerClientOptions,\n} from '@tillstack/secrets-core'\n\nexport * from '@tillstack/secrets-core'\n\n/** Create a client from options or the environment (`TILLSECRETS_TOKEN`,\n * optional `TILLSECRETS_URL`, optional `TILLSECRETS_ENV`). */\nexport function createClient(opts?: Partial<SecretsClientOptions>): SecretsClient {\n const token = opts?.token ?? process.env.TILLSECRETS_TOKEN\n if (!token) throw new Error('TillSecrets: no token — pass one or set TILLSECRETS_TOKEN')\n const built: SecretsClientOptions = { token }\n const url = opts?.url ?? process.env.TILLSECRETS_URL\n if (url) built.url = url\n const env = opts?.environmentId ?? process.env.TILLSECRETS_ENV\n if (env) built.environmentId = env\n if (opts?.timeoutMs !== undefined) built.timeoutMs = opts.timeoutMs\n if (opts?.fetch) built.fetch = opts.fetch\n return new SecretsClient(built)\n}\n\nexport interface LoadOptions extends Partial<SecretsClientOptions> {\n /** Overwrite keys already present in process.env (default false — existing\n * wins, matching dotenv). */\n override?: boolean\n}\n\n/** Pull secrets and inject them into process.env. Returns the pulled map. */\nexport async function load(opts: LoadOptions = {}): Promise<PullResult> {\n const client = createClient(opts)\n const result = await client.pull()\n for (const [k, v] of Object.entries(result.secrets)) {\n if (opts.override || process.env[k] === undefined) process.env[k] = v\n }\n return result\n}\n\nexport interface CreateBrokerOptions {\n /** Force a transport. Default `auto`: use the LOCAL bridge when the process runs\n * under the TillForge bridge daemon (`TILLDEV_BROKER_URL` set) — the strongest\n * posture, the value never leaves the machine — otherwise the cloud broker. */\n mode?: 'auto' | 'bridge' | 'cloud'\n /** Cloud API base (derives the spend endpoint). Ignored in bridge mode.\n * Defaults to `TILLDEV_URL` / `TILLSECRETS_URL` / `https://tilldev.dev`. */\n url?: string\n /** Bearer token — a session / `tp_` API key (cloud, `TILLDEV_TOKEN`) or the\n * bridge run token (`TILLDEV_BROKER_TOKEN`, injected by the daemon). */\n token?: string\n /** Stable agent identity. Required in cloud mode (or `TILLDEV_AGENT_KEY`). */\n agentKey?: string\n /** Session id for session-scoped grants (or `TILLDEV_SESSION`). */\n sessionId?: string\n timeoutMs?: number\n fetch?: typeof fetch\n}\n\n/**\n * Create a secretless-broker client. In `auto` mode it prefers the local bridge\n * proxy (value spent on the user's machine) when the daemon has injected\n * `TILLDEV_BROKER_URL` + `TILLDEV_BROKER_TOKEN`, and otherwise talks to the cloud\n * broker with a session / `tp_` token + an agent identity.\n *\n * import { createBroker, secretref } from '@tillstack/secrets-node'\n * const broker = createBroker({ agentKey: 'my-job' })\n * const res = await broker.fetch('https://api.stripe.com/v1/charges', {\n * method: 'POST',\n * headers: { authorization: `Bearer ${secretref('prod', 'live', 'STRIPE_KEY')}` },\n * body,\n * })\n * // res.body is the scrubbed reply — the STRIPE_KEY never entered this process.\n */\nexport function createBroker(opts: CreateBrokerOptions = {}): BrokerClient {\n const bridgeUrl = process.env.TILLDEV_BROKER_URL\n const bridgeToken = process.env.TILLDEV_BROKER_TOKEN\n const useBridge = opts.mode === 'bridge' || (opts.mode !== 'cloud' && !!bridgeUrl && !!bridgeToken)\n\n const built: BrokerClientOptions = { endpoint: '', token: '' }\n if (opts.timeoutMs !== undefined) built.timeoutMs = opts.timeoutMs\n if (opts.fetch) built.fetch = opts.fetch\n const agentKey = opts.agentKey ?? process.env.TILLDEV_AGENT_KEY\n if (agentKey) built.agentKey = agentKey\n const sessionId = opts.sessionId ?? process.env.TILLDEV_SESSION\n if (sessionId) built.sessionId = sessionId\n\n if (useBridge) {\n if (!bridgeUrl || !bridgeToken) {\n throw new SecretsError('TillSecrets broker: bridge mode needs TILLDEV_BROKER_URL + TILLDEV_BROKER_TOKEN')\n }\n built.endpoint = `${bridgeUrl.replace(/\\/$/, '')}/spend`\n built.token = opts.token ?? bridgeToken\n return new BrokerClient(built)\n }\n\n const base = (opts.url ?? process.env.TILLDEV_URL ?? process.env.TILLSECRETS_URL ?? 'https://tilldev.dev').replace(/\\/$/, '')\n const token = opts.token ?? process.env.TILLDEV_TOKEN ?? process.env.TILLSECRETS_BROKER_TOKEN\n if (!token) throw new SecretsError('TillSecrets broker: no token — pass one or set TILLDEV_TOKEN')\n if (!built.agentKey) throw new SecretsError('TillSecrets broker: cloud mode needs an agentKey (or TILLDEV_AGENT_KEY)')\n built.endpoint = `${base}/api/secrets/broker/spend`\n built.token = token\n return new BrokerClient(built)\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["join","homedir","statSync","SecretsError","readFileSync","existsSync","request","execFile","readFile","SecretsClient","BrokerClient"],"mappings":"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","file":"index.js","sourcesContent":["// @tillstack/secrets-node — TillSecrets client for Node.js.\n//\n// import { load } from '@tillstack/secrets-node'\n// await load() // reads TILLSECRETS_TOKEN, injects into process.env\n// // ... process.env.STRIPE_KEY is now populated\n//\n// With a workload identity instead of a stored token (TILLSECRETS_IDENTITY):\n// await load({ identity: '<identity id>' })\n//\n// or explicitly:\n// const client = createClient({ token })\n// const { secrets } = await client.pull()\n\nimport { execFile } from 'node:child_process'\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { request } from 'node:http'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport {\n SecretsClient,\n SecretsError,\n BrokerClient,\n type SecretsClientOptions,\n type PullResult,\n type BrokerClientOptions,\n type WorkloadEnvironment,\n} from '@tillstack/secrets-core'\n\nexport * from '@tillstack/secrets-core'\n\n/** The host key file made by `tilldev secrets host-key init`. */\nexport const DEFAULT_HOST_KEY_FILE = join(homedir(), '.tilldev', 'host-key')\n\nfunction readHostKeyFile(file: string): string {\n if (process.platform !== 'win32' && statSync(file).mode & 0o077) {\n throw new SecretsError(`TillSecrets: host key ${file} is readable by other users; run: chmod 600 ${file}`)\n }\n return readFileSync(file, 'utf8').trim()\n}\n\n/** `hostKey`, else `TILLSECRETS_HOST_KEY_FILE`, `TILLSECRETS_HOST_KEY`, then ~/.tilldev/host-key when it exists. */\nexport function resolveHostKey(opts?: Partial<SecretsClientOptions>, env: NodeJS.ProcessEnv = process.env, defaultFile = DEFAULT_HOST_KEY_FILE): string | Uint8Array | undefined {\n if (opts?.hostKey !== undefined || opts?.seal === false) return opts?.hostKey\n if (env.TILLSECRETS_HOST_KEY_FILE) return readHostKeyFile(env.TILLSECRETS_HOST_KEY_FILE)\n if (env.TILLSECRETS_HOST_KEY) return env.TILLSECRETS_HOST_KEY\n if (existsSync(defaultFile)) return readHostKeyFile(defaultFile)\n return undefined\n}\n\nfunction unixPost(socket: string, path: string, body: string): Promise<string> {\n return new Promise((ok, fail) => {\n const req = request({ socketPath: socket, path, method: 'POST', headers: { 'content-type': 'application/json' }, timeout: 3000 }, (res) => {\n let out = ''\n res.setEncoding('utf8')\n res.on('data', (d: string) => (out += d))\n res.on('end', () => ((res.statusCode ?? 0) < 300 ? ok(out) : fail(new Error(`${res.statusCode}`))))\n })\n req.on('timeout', () => req.destroy(new Error('timeout')))\n req.on('error', fail)\n req.end(body)\n })\n}\n\nfunction run(command: string, args: string[]): Promise<string> {\n return new Promise((ok, fail) => execFile(command, args, { timeout: 10_000, maxBuffer: 64 * 1024 }, (e, out) => (e ? fail(e) : ok(out))))\n}\n\n/** Where a workload identity's proof comes from on Node: the environment, token files, the Fly Machines socket, and platform CLIs. */\nexport function nodeWorkload(env: NodeJS.ProcessEnv = process.env): Omit<WorkloadEnvironment, 'fetch' | 'timeoutMs'> {\n return { env, readFile: (p) => readFile(p, 'utf8'), unixPost, run }\n}\n\n/** Create a client from options or the environment (`TILLSECRETS_TOKEN` or `TILLSECRETS_IDENTITY`,\n * optional `TILLSECRETS_URL`, `TILLSECRETS_ENV`, and the host key as in `resolveHostKey`). */\nexport function createClient(opts?: Partial<SecretsClientOptions>): SecretsClient {\n const identity = opts?.identity ?? (opts?.token ? undefined : process.env.TILLSECRETS_IDENTITY || undefined)\n const token = identity ? undefined : (opts?.token ?? process.env.TILLSECRETS_TOKEN)\n if (!token && !identity) throw new Error('TillSecrets: no token — pass one or set TILLSECRETS_TOKEN (or TILLSECRETS_IDENTITY for a workload identity)')\n const built: SecretsClientOptions = identity ? { ...opts, identity, workload: { ...nodeWorkload(), ...opts?.workload } } : { ...opts, token: token! }\n const url = opts?.url ?? process.env.TILLSECRETS_URL\n if (url) built.url = url\n const leaseUrl = opts?.leaseUrl ?? process.env.TILLSECRETS_LEASE_URL\n if (leaseUrl) built.leaseUrl = leaseUrl\n const env = opts?.environmentId ?? process.env.TILLSECRETS_ENV\n if (env) built.environmentId = env\n const hostKey = resolveHostKey(opts)\n if (hostKey !== undefined) built.hostKey = hostKey\n return new SecretsClient(built)\n}\n\nexport interface LoadOptions extends Partial<SecretsClientOptions> {\n /** Overwrite keys already present in process.env (default false — existing\n * wins, matching dotenv). */\n override?: boolean\n /** Inject what was returned even if restricted or sealed keys were withheld (default false — throw). */\n allowPartial?: boolean\n}\n\n/** Pull secrets and inject them into process.env. Returns the pulled map. */\nexport async function load(opts: LoadOptions = {}): Promise<PullResult> {\n const client = createClient(opts)\n const result = await client.pull()\n if (result.withheld.length && !opts.allowPartial) {\n throw new SecretsError(\n `TillSecrets: withheld restricted or sealed key(s) ${result.withheld.join(', ')} — they need a token pinned to this environment (or pass allowPartial)`,\n 403,\n )\n }\n for (const [k, v] of Object.entries(result.secrets)) {\n if (opts.override || process.env[k] === undefined) process.env[k] = v\n }\n return result\n}\n\nexport interface CreateBrokerOptions {\n /** Force a transport. Default `auto`: use the LOCAL bridge when the process runs\n * under the TillForge bridge daemon (`TILLDEV_BROKER_URL` set) — the strongest\n * posture, the value never leaves the machine — otherwise the cloud broker. */\n mode?: 'auto' | 'bridge' | 'cloud'\n /** Cloud API base (derives the spend endpoint). Ignored in bridge mode.\n * Defaults to `TILLDEV_URL` / `TILLSECRETS_URL` / `https://tilldev.dev`. */\n url?: string\n /** Bearer token: an agent token (`ta_`, `TILLDEV_AGENT_TOKEN`), a session / `tp_` API key\n * (`TILLDEV_TOKEN`), or the bridge run token (`TILLDEV_BROKER_TOKEN`, injected by the daemon). */\n token?: string\n /** Stable agent identity. Required in cloud mode unless the token is a `ta_` agent token, which names itself. */\n agentKey?: string\n /** Session id for session-scoped grants (or `TILLDEV_SESSION`). */\n sessionId?: string\n timeoutMs?: number\n fetch?: typeof fetch\n}\n\n/**\n * Create a secretless-broker client. In `auto` mode it prefers the local bridge\n * proxy (value spent on the user's machine) when the daemon has injected\n * `TILLDEV_BROKER_URL` + `TILLDEV_BROKER_TOKEN`, and otherwise talks to the cloud\n * broker with a session / `tp_` token + an agent identity.\n *\n * import { createBroker, secretref } from '@tillstack/secrets-node'\n * const broker = createBroker({ agentKey: 'my-job' })\n * const res = await broker.fetch('https://api.stripe.com/v1/charges', {\n * method: 'POST',\n * headers: { authorization: `Bearer ${secretref('prod', 'live', 'STRIPE_KEY')}` },\n * body,\n * })\n * // res.body is the scrubbed reply — the STRIPE_KEY never entered this process.\n */\nexport function createBroker(opts: CreateBrokerOptions = {}): BrokerClient {\n const bridgeUrl = process.env.TILLDEV_BROKER_URL\n const bridgeToken = process.env.TILLDEV_BROKER_TOKEN\n const useBridge = opts.mode === 'bridge' || (opts.mode !== 'cloud' && !!bridgeUrl && !!bridgeToken)\n\n const built: BrokerClientOptions = { endpoint: '', token: '' }\n if (opts.timeoutMs !== undefined) built.timeoutMs = opts.timeoutMs\n if (opts.fetch) built.fetch = opts.fetch\n const agentKey = opts.agentKey ?? process.env.TILLDEV_AGENT_KEY\n if (agentKey) built.agentKey = agentKey\n const sessionId = opts.sessionId ?? process.env.TILLDEV_SESSION\n if (sessionId) built.sessionId = sessionId\n\n if (useBridge) {\n if (!bridgeUrl || !bridgeToken) {\n throw new SecretsError('TillSecrets broker: bridge mode needs TILLDEV_BROKER_URL + TILLDEV_BROKER_TOKEN')\n }\n built.endpoint = `${bridgeUrl.replace(/\\/$/, '')}/spend`\n built.token = opts.token ?? bridgeToken\n return new BrokerClient(built)\n }\n\n const base = (opts.url ?? process.env.TILLDEV_URL ?? process.env.TILLSECRETS_URL ?? 'https://tilldev.dev').replace(/\\/$/, '')\n const token = opts.token || process.env.TILLDEV_AGENT_TOKEN || process.env.TILLDEV_TOKEN || process.env.TILLSECRETS_BROKER_TOKEN\n if (!token) throw new SecretsError('TillSecrets broker: no token — pass one or set TILLDEV_AGENT_TOKEN')\n if (!built.agentKey && !token.startsWith('ta_')) {\n throw new SecretsError('TillSecrets broker: cloud mode needs an agentKey (or TILLDEV_AGENT_KEY), or a ta_ agent token')\n }\n built.endpoint = `${base}/api/secrets/broker/spend`\n built.token = token\n return new BrokerClient(built)\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,22 +1,70 @@
|
|
|
1
|
+
import { execFile } from 'child_process';
|
|
2
|
+
import { existsSync, statSync, readFileSync } from 'fs';
|
|
3
|
+
import { readFile } from 'fs/promises';
|
|
4
|
+
import { request } from 'http';
|
|
5
|
+
import { homedir } from 'os';
|
|
6
|
+
import { join } from 'path';
|
|
1
7
|
import { SecretsClient, SecretsError, BrokerClient } from '@tillstack/secrets-core';
|
|
2
8
|
export * from '@tillstack/secrets-core';
|
|
3
9
|
|
|
4
10
|
// src/index.ts
|
|
11
|
+
var DEFAULT_HOST_KEY_FILE = join(homedir(), ".tilldev", "host-key");
|
|
12
|
+
function readHostKeyFile(file) {
|
|
13
|
+
if (process.platform !== "win32" && statSync(file).mode & 63) {
|
|
14
|
+
throw new SecretsError(`TillSecrets: host key ${file} is readable by other users; run: chmod 600 ${file}`);
|
|
15
|
+
}
|
|
16
|
+
return readFileSync(file, "utf8").trim();
|
|
17
|
+
}
|
|
18
|
+
function resolveHostKey(opts, env = process.env, defaultFile = DEFAULT_HOST_KEY_FILE) {
|
|
19
|
+
if (opts?.hostKey !== void 0 || opts?.seal === false) return opts?.hostKey;
|
|
20
|
+
if (env.TILLSECRETS_HOST_KEY_FILE) return readHostKeyFile(env.TILLSECRETS_HOST_KEY_FILE);
|
|
21
|
+
if (env.TILLSECRETS_HOST_KEY) return env.TILLSECRETS_HOST_KEY;
|
|
22
|
+
if (existsSync(defaultFile)) return readHostKeyFile(defaultFile);
|
|
23
|
+
return void 0;
|
|
24
|
+
}
|
|
25
|
+
function unixPost(socket, path, body) {
|
|
26
|
+
return new Promise((ok, fail) => {
|
|
27
|
+
const req = request({ socketPath: socket, path, method: "POST", headers: { "content-type": "application/json" }, timeout: 3e3 }, (res) => {
|
|
28
|
+
let out = "";
|
|
29
|
+
res.setEncoding("utf8");
|
|
30
|
+
res.on("data", (d) => out += d);
|
|
31
|
+
res.on("end", () => (res.statusCode ?? 0) < 300 ? ok(out) : fail(new Error(`${res.statusCode}`)));
|
|
32
|
+
});
|
|
33
|
+
req.on("timeout", () => req.destroy(new Error("timeout")));
|
|
34
|
+
req.on("error", fail);
|
|
35
|
+
req.end(body);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function run(command, args) {
|
|
39
|
+
return new Promise((ok, fail) => execFile(command, args, { timeout: 1e4, maxBuffer: 64 * 1024 }, (e, out) => e ? fail(e) : ok(out)));
|
|
40
|
+
}
|
|
41
|
+
function nodeWorkload(env = process.env) {
|
|
42
|
+
return { env, readFile: (p) => readFile(p, "utf8"), unixPost, run };
|
|
43
|
+
}
|
|
5
44
|
function createClient(opts) {
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
45
|
+
const identity = opts?.identity ?? (opts?.token ? void 0 : process.env.TILLSECRETS_IDENTITY || void 0);
|
|
46
|
+
const token = identity ? void 0 : opts?.token ?? process.env.TILLSECRETS_TOKEN;
|
|
47
|
+
if (!token && !identity) throw new Error("TillSecrets: no token \u2014 pass one or set TILLSECRETS_TOKEN (or TILLSECRETS_IDENTITY for a workload identity)");
|
|
48
|
+
const built = identity ? { ...opts, identity, workload: { ...nodeWorkload(), ...opts?.workload } } : { ...opts, token };
|
|
9
49
|
const url = opts?.url ?? process.env.TILLSECRETS_URL;
|
|
10
50
|
if (url) built.url = url;
|
|
51
|
+
const leaseUrl = opts?.leaseUrl ?? process.env.TILLSECRETS_LEASE_URL;
|
|
52
|
+
if (leaseUrl) built.leaseUrl = leaseUrl;
|
|
11
53
|
const env = opts?.environmentId ?? process.env.TILLSECRETS_ENV;
|
|
12
54
|
if (env) built.environmentId = env;
|
|
13
|
-
|
|
14
|
-
if (
|
|
55
|
+
const hostKey = resolveHostKey(opts);
|
|
56
|
+
if (hostKey !== void 0) built.hostKey = hostKey;
|
|
15
57
|
return new SecretsClient(built);
|
|
16
58
|
}
|
|
17
59
|
async function load(opts = {}) {
|
|
18
60
|
const client = createClient(opts);
|
|
19
61
|
const result = await client.pull();
|
|
62
|
+
if (result.withheld.length && !opts.allowPartial) {
|
|
63
|
+
throw new SecretsError(
|
|
64
|
+
`TillSecrets: withheld restricted or sealed key(s) ${result.withheld.join(", ")} \u2014 they need a token pinned to this environment (or pass allowPartial)`,
|
|
65
|
+
403
|
|
66
|
+
);
|
|
67
|
+
}
|
|
20
68
|
for (const [k, v] of Object.entries(result.secrets)) {
|
|
21
69
|
if (opts.override || process.env[k] === void 0) process.env[k] = v;
|
|
22
70
|
}
|
|
@@ -42,14 +90,16 @@ function createBroker(opts = {}) {
|
|
|
42
90
|
return new BrokerClient(built);
|
|
43
91
|
}
|
|
44
92
|
const base = (opts.url ?? process.env.TILLDEV_URL ?? process.env.TILLSECRETS_URL ?? "https://tilldev.dev").replace(/\/$/, "");
|
|
45
|
-
const token = opts.token
|
|
46
|
-
if (!token) throw new SecretsError("TillSecrets broker: no token \u2014 pass one or set
|
|
47
|
-
if (!built.agentKey
|
|
93
|
+
const token = opts.token || process.env.TILLDEV_AGENT_TOKEN || process.env.TILLDEV_TOKEN || process.env.TILLSECRETS_BROKER_TOKEN;
|
|
94
|
+
if (!token) throw new SecretsError("TillSecrets broker: no token \u2014 pass one or set TILLDEV_AGENT_TOKEN");
|
|
95
|
+
if (!built.agentKey && !token.startsWith("ta_")) {
|
|
96
|
+
throw new SecretsError("TillSecrets broker: cloud mode needs an agentKey (or TILLDEV_AGENT_KEY), or a ta_ agent token");
|
|
97
|
+
}
|
|
48
98
|
built.endpoint = `${base}/api/secrets/broker/spend`;
|
|
49
99
|
built.token = token;
|
|
50
100
|
return new BrokerClient(built);
|
|
51
101
|
}
|
|
52
102
|
|
|
53
|
-
export { createBroker, createClient, load };
|
|
103
|
+
export { DEFAULT_HOST_KEY_FILE, createBroker, createClient, load, nodeWorkload, resolveHostKey };
|
|
54
104
|
//# sourceMappingURL=index.mjs.map
|
|
55
105
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["// @tillstack/secrets-node — TillSecrets client for Node.js.\n//\n// import { load } from '@tillstack/secrets-node'\n// await load() // reads TILLSECRETS_TOKEN, injects into process.env\n// // ... process.env.STRIPE_KEY is now populated\n//\n// or explicitly:\n// const client = createClient({ token })\n// const { secrets } = await client.pull()\n\nimport {\n SecretsClient,\n SecretsError,\n BrokerClient,\n type SecretsClientOptions,\n type PullResult,\n type BrokerClientOptions,\n} from '@tillstack/secrets-core'\n\nexport * from '@tillstack/secrets-core'\n\n/** Create a client from options or the environment (`TILLSECRETS_TOKEN`,\n * optional `TILLSECRETS_URL`, optional `TILLSECRETS_ENV`). */\nexport function createClient(opts?: Partial<SecretsClientOptions>): SecretsClient {\n const token = opts?.token ?? process.env.TILLSECRETS_TOKEN\n if (!token) throw new Error('TillSecrets: no token — pass one or set TILLSECRETS_TOKEN')\n const built: SecretsClientOptions = { token }\n const url = opts?.url ?? process.env.TILLSECRETS_URL\n if (url) built.url = url\n const env = opts?.environmentId ?? process.env.TILLSECRETS_ENV\n if (env) built.environmentId = env\n if (opts?.timeoutMs !== undefined) built.timeoutMs = opts.timeoutMs\n if (opts?.fetch) built.fetch = opts.fetch\n return new SecretsClient(built)\n}\n\nexport interface LoadOptions extends Partial<SecretsClientOptions> {\n /** Overwrite keys already present in process.env (default false — existing\n * wins, matching dotenv). */\n override?: boolean\n}\n\n/** Pull secrets and inject them into process.env. Returns the pulled map. */\nexport async function load(opts: LoadOptions = {}): Promise<PullResult> {\n const client = createClient(opts)\n const result = await client.pull()\n for (const [k, v] of Object.entries(result.secrets)) {\n if (opts.override || process.env[k] === undefined) process.env[k] = v\n }\n return result\n}\n\nexport interface CreateBrokerOptions {\n /** Force a transport. Default `auto`: use the LOCAL bridge when the process runs\n * under the TillForge bridge daemon (`TILLDEV_BROKER_URL` set) — the strongest\n * posture, the value never leaves the machine — otherwise the cloud broker. */\n mode?: 'auto' | 'bridge' | 'cloud'\n /** Cloud API base (derives the spend endpoint). Ignored in bridge mode.\n * Defaults to `TILLDEV_URL` / `TILLSECRETS_URL` / `https://tilldev.dev`. */\n url?: string\n /** Bearer token — a session / `tp_` API key (cloud, `TILLDEV_TOKEN`) or the\n * bridge run token (`TILLDEV_BROKER_TOKEN`, injected by the daemon). */\n token?: string\n /** Stable agent identity. Required in cloud mode (or `TILLDEV_AGENT_KEY`). */\n agentKey?: string\n /** Session id for session-scoped grants (or `TILLDEV_SESSION`). */\n sessionId?: string\n timeoutMs?: number\n fetch?: typeof fetch\n}\n\n/**\n * Create a secretless-broker client. In `auto` mode it prefers the local bridge\n * proxy (value spent on the user's machine) when the daemon has injected\n * `TILLDEV_BROKER_URL` + `TILLDEV_BROKER_TOKEN`, and otherwise talks to the cloud\n * broker with a session / `tp_` token + an agent identity.\n *\n * import { createBroker, secretref } from '@tillstack/secrets-node'\n * const broker = createBroker({ agentKey: 'my-job' })\n * const res = await broker.fetch('https://api.stripe.com/v1/charges', {\n * method: 'POST',\n * headers: { authorization: `Bearer ${secretref('prod', 'live', 'STRIPE_KEY')}` },\n * body,\n * })\n * // res.body is the scrubbed reply — the STRIPE_KEY never entered this process.\n */\nexport function createBroker(opts: CreateBrokerOptions = {}): BrokerClient {\n const bridgeUrl = process.env.TILLDEV_BROKER_URL\n const bridgeToken = process.env.TILLDEV_BROKER_TOKEN\n const useBridge = opts.mode === 'bridge' || (opts.mode !== 'cloud' && !!bridgeUrl && !!bridgeToken)\n\n const built: BrokerClientOptions = { endpoint: '', token: '' }\n if (opts.timeoutMs !== undefined) built.timeoutMs = opts.timeoutMs\n if (opts.fetch) built.fetch = opts.fetch\n const agentKey = opts.agentKey ?? process.env.TILLDEV_AGENT_KEY\n if (agentKey) built.agentKey = agentKey\n const sessionId = opts.sessionId ?? process.env.TILLDEV_SESSION\n if (sessionId) built.sessionId = sessionId\n\n if (useBridge) {\n if (!bridgeUrl || !bridgeToken) {\n throw new SecretsError('TillSecrets broker: bridge mode needs TILLDEV_BROKER_URL + TILLDEV_BROKER_TOKEN')\n }\n built.endpoint = `${bridgeUrl.replace(/\\/$/, '')}/spend`\n built.token = opts.token ?? bridgeToken\n return new BrokerClient(built)\n }\n\n const base = (opts.url ?? process.env.TILLDEV_URL ?? process.env.TILLSECRETS_URL ?? 'https://tilldev.dev').replace(/\\/$/, '')\n const token = opts.token ?? process.env.TILLDEV_TOKEN ?? process.env.TILLSECRETS_BROKER_TOKEN\n if (!token) throw new SecretsError('TillSecrets broker: no token — pass one or set TILLDEV_TOKEN')\n if (!built.agentKey) throw new SecretsError('TillSecrets broker: cloud mode needs an agentKey (or TILLDEV_AGENT_KEY)')\n built.endpoint = `${base}/api/secrets/broker/spend`\n built.token = token\n return new BrokerClient(built)\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["// @tillstack/secrets-node — TillSecrets client for Node.js.\n//\n// import { load } from '@tillstack/secrets-node'\n// await load() // reads TILLSECRETS_TOKEN, injects into process.env\n// // ... process.env.STRIPE_KEY is now populated\n//\n// With a workload identity instead of a stored token (TILLSECRETS_IDENTITY):\n// await load({ identity: '<identity id>' })\n//\n// or explicitly:\n// const client = createClient({ token })\n// const { secrets } = await client.pull()\n\nimport { execFile } from 'node:child_process'\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { request } from 'node:http'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport {\n SecretsClient,\n SecretsError,\n BrokerClient,\n type SecretsClientOptions,\n type PullResult,\n type BrokerClientOptions,\n type WorkloadEnvironment,\n} from '@tillstack/secrets-core'\n\nexport * from '@tillstack/secrets-core'\n\n/** The host key file made by `tilldev secrets host-key init`. */\nexport const DEFAULT_HOST_KEY_FILE = join(homedir(), '.tilldev', 'host-key')\n\nfunction readHostKeyFile(file: string): string {\n if (process.platform !== 'win32' && statSync(file).mode & 0o077) {\n throw new SecretsError(`TillSecrets: host key ${file} is readable by other users; run: chmod 600 ${file}`)\n }\n return readFileSync(file, 'utf8').trim()\n}\n\n/** `hostKey`, else `TILLSECRETS_HOST_KEY_FILE`, `TILLSECRETS_HOST_KEY`, then ~/.tilldev/host-key when it exists. */\nexport function resolveHostKey(opts?: Partial<SecretsClientOptions>, env: NodeJS.ProcessEnv = process.env, defaultFile = DEFAULT_HOST_KEY_FILE): string | Uint8Array | undefined {\n if (opts?.hostKey !== undefined || opts?.seal === false) return opts?.hostKey\n if (env.TILLSECRETS_HOST_KEY_FILE) return readHostKeyFile(env.TILLSECRETS_HOST_KEY_FILE)\n if (env.TILLSECRETS_HOST_KEY) return env.TILLSECRETS_HOST_KEY\n if (existsSync(defaultFile)) return readHostKeyFile(defaultFile)\n return undefined\n}\n\nfunction unixPost(socket: string, path: string, body: string): Promise<string> {\n return new Promise((ok, fail) => {\n const req = request({ socketPath: socket, path, method: 'POST', headers: { 'content-type': 'application/json' }, timeout: 3000 }, (res) => {\n let out = ''\n res.setEncoding('utf8')\n res.on('data', (d: string) => (out += d))\n res.on('end', () => ((res.statusCode ?? 0) < 300 ? ok(out) : fail(new Error(`${res.statusCode}`))))\n })\n req.on('timeout', () => req.destroy(new Error('timeout')))\n req.on('error', fail)\n req.end(body)\n })\n}\n\nfunction run(command: string, args: string[]): Promise<string> {\n return new Promise((ok, fail) => execFile(command, args, { timeout: 10_000, maxBuffer: 64 * 1024 }, (e, out) => (e ? fail(e) : ok(out))))\n}\n\n/** Where a workload identity's proof comes from on Node: the environment, token files, the Fly Machines socket, and platform CLIs. */\nexport function nodeWorkload(env: NodeJS.ProcessEnv = process.env): Omit<WorkloadEnvironment, 'fetch' | 'timeoutMs'> {\n return { env, readFile: (p) => readFile(p, 'utf8'), unixPost, run }\n}\n\n/** Create a client from options or the environment (`TILLSECRETS_TOKEN` or `TILLSECRETS_IDENTITY`,\n * optional `TILLSECRETS_URL`, `TILLSECRETS_ENV`, and the host key as in `resolveHostKey`). */\nexport function createClient(opts?: Partial<SecretsClientOptions>): SecretsClient {\n const identity = opts?.identity ?? (opts?.token ? undefined : process.env.TILLSECRETS_IDENTITY || undefined)\n const token = identity ? undefined : (opts?.token ?? process.env.TILLSECRETS_TOKEN)\n if (!token && !identity) throw new Error('TillSecrets: no token — pass one or set TILLSECRETS_TOKEN (or TILLSECRETS_IDENTITY for a workload identity)')\n const built: SecretsClientOptions = identity ? { ...opts, identity, workload: { ...nodeWorkload(), ...opts?.workload } } : { ...opts, token: token! }\n const url = opts?.url ?? process.env.TILLSECRETS_URL\n if (url) built.url = url\n const leaseUrl = opts?.leaseUrl ?? process.env.TILLSECRETS_LEASE_URL\n if (leaseUrl) built.leaseUrl = leaseUrl\n const env = opts?.environmentId ?? process.env.TILLSECRETS_ENV\n if (env) built.environmentId = env\n const hostKey = resolveHostKey(opts)\n if (hostKey !== undefined) built.hostKey = hostKey\n return new SecretsClient(built)\n}\n\nexport interface LoadOptions extends Partial<SecretsClientOptions> {\n /** Overwrite keys already present in process.env (default false — existing\n * wins, matching dotenv). */\n override?: boolean\n /** Inject what was returned even if restricted or sealed keys were withheld (default false — throw). */\n allowPartial?: boolean\n}\n\n/** Pull secrets and inject them into process.env. Returns the pulled map. */\nexport async function load(opts: LoadOptions = {}): Promise<PullResult> {\n const client = createClient(opts)\n const result = await client.pull()\n if (result.withheld.length && !opts.allowPartial) {\n throw new SecretsError(\n `TillSecrets: withheld restricted or sealed key(s) ${result.withheld.join(', ')} — they need a token pinned to this environment (or pass allowPartial)`,\n 403,\n )\n }\n for (const [k, v] of Object.entries(result.secrets)) {\n if (opts.override || process.env[k] === undefined) process.env[k] = v\n }\n return result\n}\n\nexport interface CreateBrokerOptions {\n /** Force a transport. Default `auto`: use the LOCAL bridge when the process runs\n * under the TillForge bridge daemon (`TILLDEV_BROKER_URL` set) — the strongest\n * posture, the value never leaves the machine — otherwise the cloud broker. */\n mode?: 'auto' | 'bridge' | 'cloud'\n /** Cloud API base (derives the spend endpoint). Ignored in bridge mode.\n * Defaults to `TILLDEV_URL` / `TILLSECRETS_URL` / `https://tilldev.dev`. */\n url?: string\n /** Bearer token: an agent token (`ta_`, `TILLDEV_AGENT_TOKEN`), a session / `tp_` API key\n * (`TILLDEV_TOKEN`), or the bridge run token (`TILLDEV_BROKER_TOKEN`, injected by the daemon). */\n token?: string\n /** Stable agent identity. Required in cloud mode unless the token is a `ta_` agent token, which names itself. */\n agentKey?: string\n /** Session id for session-scoped grants (or `TILLDEV_SESSION`). */\n sessionId?: string\n timeoutMs?: number\n fetch?: typeof fetch\n}\n\n/**\n * Create a secretless-broker client. In `auto` mode it prefers the local bridge\n * proxy (value spent on the user's machine) when the daemon has injected\n * `TILLDEV_BROKER_URL` + `TILLDEV_BROKER_TOKEN`, and otherwise talks to the cloud\n * broker with a session / `tp_` token + an agent identity.\n *\n * import { createBroker, secretref } from '@tillstack/secrets-node'\n * const broker = createBroker({ agentKey: 'my-job' })\n * const res = await broker.fetch('https://api.stripe.com/v1/charges', {\n * method: 'POST',\n * headers: { authorization: `Bearer ${secretref('prod', 'live', 'STRIPE_KEY')}` },\n * body,\n * })\n * // res.body is the scrubbed reply — the STRIPE_KEY never entered this process.\n */\nexport function createBroker(opts: CreateBrokerOptions = {}): BrokerClient {\n const bridgeUrl = process.env.TILLDEV_BROKER_URL\n const bridgeToken = process.env.TILLDEV_BROKER_TOKEN\n const useBridge = opts.mode === 'bridge' || (opts.mode !== 'cloud' && !!bridgeUrl && !!bridgeToken)\n\n const built: BrokerClientOptions = { endpoint: '', token: '' }\n if (opts.timeoutMs !== undefined) built.timeoutMs = opts.timeoutMs\n if (opts.fetch) built.fetch = opts.fetch\n const agentKey = opts.agentKey ?? process.env.TILLDEV_AGENT_KEY\n if (agentKey) built.agentKey = agentKey\n const sessionId = opts.sessionId ?? process.env.TILLDEV_SESSION\n if (sessionId) built.sessionId = sessionId\n\n if (useBridge) {\n if (!bridgeUrl || !bridgeToken) {\n throw new SecretsError('TillSecrets broker: bridge mode needs TILLDEV_BROKER_URL + TILLDEV_BROKER_TOKEN')\n }\n built.endpoint = `${bridgeUrl.replace(/\\/$/, '')}/spend`\n built.token = opts.token ?? bridgeToken\n return new BrokerClient(built)\n }\n\n const base = (opts.url ?? process.env.TILLDEV_URL ?? process.env.TILLSECRETS_URL ?? 'https://tilldev.dev').replace(/\\/$/, '')\n const token = opts.token || process.env.TILLDEV_AGENT_TOKEN || process.env.TILLDEV_TOKEN || process.env.TILLSECRETS_BROKER_TOKEN\n if (!token) throw new SecretsError('TillSecrets broker: no token — pass one or set TILLDEV_AGENT_TOKEN')\n if (!built.agentKey && !token.startsWith('ta_')) {\n throw new SecretsError('TillSecrets broker: cloud mode needs an agentKey (or TILLDEV_AGENT_KEY), or a ta_ agent token')\n }\n built.endpoint = `${base}/api/secrets/broker/spend`\n built.token = token\n return new BrokerClient(built)\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tillstack/secrets-node",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "TillSecrets client for Node.js — pull encrypted config at boot and inject it into process.env, with a drop-in dotenv-style API.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"dist"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@tillstack/secrets-core": "^0.
|
|
27
|
+
"@tillstack/secrets-core": "^0.4.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^22.0.0",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"build": "tsup",
|
|
58
58
|
"dev": "tsup --watch",
|
|
59
59
|
"typecheck": "tsc --noEmit",
|
|
60
|
-
"clean": "rm -rf dist"
|
|
60
|
+
"clean": "rm -rf dist",
|
|
61
|
+
"test": "tsup && node --test test/*.test.mjs"
|
|
61
62
|
}
|
|
62
63
|
}
|