@reegaviljoen/eldlock 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.
- package/README.md +285 -0
- package/bin/eldlock +11 -0
- package/docs/architecture.md +164 -0
- package/docs/threat-model.md +47 -0
- package/eldlock-cli/README.md +56 -0
- package/eldlock-cli/bin/eldlock +3 -0
- package/eldlock-cli/package-lock.json +805 -0
- package/eldlock-cli/package.json +71 -0
- package/eldlock-cli/src/api.ts +250 -0
- package/eldlock-cli/src/cli.ts +490 -0
- package/eldlock-cli/src/main.ts +10 -0
- package/eldlock-cli/src/tui.ts +676 -0
- package/eldlock-cli/tsconfig.json +13 -0
- package/eldlock-cli/vendor/npm/ansi-regex-6.2.2.tgz +0 -0
- package/eldlock-cli/vendor/npm/bun-ffi-structs-0.2.2.tgz +0 -0
- package/eldlock-cli/vendor/npm/diff-9.0.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/emoji-regex-10.6.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-darwin-arm64-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-darwin-x64-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-linux-arm64-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/esbuild-linux-x64-0.28.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/fsevents-2.3.3.tgz +0 -0
- package/eldlock-cli/vendor/npm/get-east-asian-width-1.6.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/marked-17.0.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-darwin-arm64-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-darwin-x64-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-linux-arm64-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/opentui-core-linux-x64-0.3.1.tgz +0 -0
- package/eldlock-cli/vendor/npm/string-width-7.2.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/strip-ansi-7.1.2.tgz +0 -0
- package/eldlock-cli/vendor/npm/tsx-4.22.4.tgz +0 -0
- package/eldlock-cli/vendor/npm/types-node-22.19.19.tgz +0 -0
- package/eldlock-cli/vendor/npm/typescript-5.9.3.tgz +0 -0
- package/eldlock-cli/vendor/npm/undici-types-6.21.0.tgz +0 -0
- package/eldlock-cli/vendor/npm/web-tree-sitter-0.25.10.tgz +0 -0
- package/eldlock-cli/vendor/npm/yoga-layout-3.2.1.tgz +0 -0
- package/eldlock-server/cmd/eldlock-server/main.go +132 -0
- package/eldlock-server/go.mod +10 -0
- package/eldlock-server/go.sum +11 -0
- package/eldlock-server/internal/api/README.md +14 -0
- package/eldlock-server/internal/api/core.go +126 -0
- package/eldlock-server/internal/api/exec.go +97 -0
- package/eldlock-server/internal/api/secrets.go +358 -0
- package/eldlock-server/internal/api/server.go +72 -0
- package/eldlock-server/internal/api/service_test.go +416 -0
- package/eldlock-server/internal/api/types.go +48 -0
- package/eldlock-server/internal/api/vault.go +69 -0
- package/eldlock-server/internal/api/vendor.go +44 -0
- package/eldlock-server/internal/libfido2/LICENSE +21 -0
- package/eldlock-server/internal/libfido2/README.md +127 -0
- package/eldlock-server/internal/libfido2/examples_test.go +614 -0
- package/eldlock-server/internal/libfido2/fido2.go +1234 -0
- package/eldlock-server/internal/libfido2/fido2_darwin.go +7 -0
- package/eldlock-server/internal/libfido2/fido2_other.go +9 -0
- package/eldlock-server/internal/libfido2/fido2_test.go +101 -0
- package/eldlock-server/internal/libfido2/go.mod +10 -0
- package/eldlock-server/internal/libfido2/go.sum +16 -0
- package/eldlock-server/internal/libfido2/log.go +87 -0
- package/eldlock-server/internal/store/README.md +7 -0
- package/eldlock-server/internal/store/store.go +434 -0
- package/eldlock-server/internal/store/store_test.go +125 -0
- package/eldlock-server/internal/yubikey/README.md +25 -0
- package/eldlock-server/internal/yubikey/default_fido2.go +7 -0
- package/eldlock-server/internal/yubikey/default_stub.go +7 -0
- package/eldlock-server/internal/yubikey/fido2_disabled.go +9 -0
- package/eldlock-server/internal/yubikey/fido2_libfido2.go +225 -0
- package/eldlock-server/internal/yubikey/fido2_libfido2_test.go +66 -0
- package/eldlock-server/internal/yubikey/passkey.go +139 -0
- package/eldlock-server/internal/yubikey/passkey_test.go +36 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/LICENSE +21 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/README.md +127 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/fido2.go +1234 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/fido2_darwin.go +7 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/fido2_other.go +9 -0
- package/eldlock-server/vendor/github.com/keys-pub/go-libfido2/log.go +87 -0
- package/eldlock-server/vendor/github.com/pkg/errors/.travis.yml +10 -0
- package/eldlock-server/vendor/github.com/pkg/errors/LICENSE +23 -0
- package/eldlock-server/vendor/github.com/pkg/errors/Makefile +44 -0
- package/eldlock-server/vendor/github.com/pkg/errors/README.md +59 -0
- package/eldlock-server/vendor/github.com/pkg/errors/appveyor.yml +32 -0
- package/eldlock-server/vendor/github.com/pkg/errors/errors.go +288 -0
- package/eldlock-server/vendor/github.com/pkg/errors/go113.go +38 -0
- package/eldlock-server/vendor/github.com/pkg/errors/stack.go +177 -0
- package/eldlock-server/vendor/modules.txt +7 -0
- package/examples/eldlock.toml +17 -0
- package/install.sh +66 -0
- package/package.json +66 -0
- package/scripts/build-production.mjs +177 -0
- package/scripts/postinstall-production.mjs +23 -0
package/README.md
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Eldlock
|
|
2
|
+
|
|
3
|
+
Eldlock is a local-first secret broker for developer machines.
|
|
4
|
+
|
|
5
|
+
It keeps developer secrets out of long-lived plaintext files, shell profiles,
|
|
6
|
+
and extractable private-key paths while preserving the interfaces developers
|
|
7
|
+
already use: `SSH_AUTH_SOCK`, shells, env vars, `.env` workflows, process
|
|
8
|
+
launchers, and local credential helpers.
|
|
9
|
+
|
|
10
|
+
Eldlock is not a hosted secrets manager. The vault lives on your machine,
|
|
11
|
+
secrets are encrypted on your machine, and normal secret release does not need
|
|
12
|
+
the internet.
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
eldlock CLI / TUI
|
|
16
|
+
|
|
|
17
|
+
| local Unix socket
|
|
18
|
+
v
|
|
19
|
+
eldlock-server local daemon
|
|
20
|
+
|
|
|
21
|
+
v
|
|
22
|
+
local encrypted vault
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
Eldlock currently builds from source on the target machine. The installer uses
|
|
28
|
+
vendored Go and npm dependencies after it downloads the repository archive.
|
|
29
|
+
|
|
30
|
+
Install prerequisites first:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# macOS
|
|
34
|
+
brew install go node pkg-config keys-pub/tap/libfido2
|
|
35
|
+
curl -fsSL https://bun.sh/install | bash
|
|
36
|
+
|
|
37
|
+
# Ubuntu / Debian
|
|
38
|
+
sudo apt update
|
|
39
|
+
sudo apt install golang nodejs npm pkg-config libfido2-dev
|
|
40
|
+
curl -fsSL https://bun.sh/install | bash
|
|
41
|
+
|
|
42
|
+
# Red Hat / Fedora
|
|
43
|
+
sudo dnf install golang nodejs npm pkgconf-pkg-config libfido2-devel
|
|
44
|
+
curl -fsSL https://bun.sh/install | bash
|
|
45
|
+
|
|
46
|
+
# Arch Linux
|
|
47
|
+
sudo pacman -Syu go nodejs npm pkgconf libfido2
|
|
48
|
+
curl -fsSL https://bun.sh/install | bash
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Then run the installer:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
curl -fsSL https://raw.githubusercontent.com/reeganviljoen/eldlock/main/install.sh | bash
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or install from npm after the package is published:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install -g @reegaviljoen/eldlock
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
If the repository is private, pass a GitHub token that can read it:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
export GITHUB_TOKEN="github_pat_or_ghp_token_with_repo_read_access"
|
|
67
|
+
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
68
|
+
https://raw.githubusercontent.com/reeganviljoen/eldlock/main/install.sh | bash
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The default install writes:
|
|
72
|
+
|
|
73
|
+
- `~/.local/bin/eldlock`
|
|
74
|
+
- `~/.local/share/eldlock/bin/eldlock-server`
|
|
75
|
+
- `~/.local/share/eldlock/lib/eldlock-cli`
|
|
76
|
+
|
|
77
|
+
Add the command directory to your shell path if needed:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Initialize and check the daemon:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
eldlock start
|
|
87
|
+
eldlock init
|
|
88
|
+
eldlock status
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`eldlock` uses `~/.eldlock` as its default state directory. Override it with
|
|
92
|
+
`ELDLOCK_STATE_DIR` when you want isolated state for testing.
|
|
93
|
+
|
|
94
|
+
## Use
|
|
95
|
+
|
|
96
|
+
Store and read a secret:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
eldlock secret add personal/openai/api_key=sk-test
|
|
100
|
+
eldlock secret list
|
|
101
|
+
eldlock secret read personal/openai/api_key
|
|
102
|
+
eldlock secret read personal/openai/api_key --plain
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Import env-style secrets and launch a process with injected env vars:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
eldlock import .env
|
|
109
|
+
eldlock exec -- rails s
|
|
110
|
+
eldlock exec -- 'echo "$NODE_AUTH_TOKEN"'
|
|
111
|
+
eldlock shell --vault eldlock_secrets
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The enclosed command runs through your current shell and receives normal env
|
|
115
|
+
vars. The difference is that the secret is released just in time by the daemon
|
|
116
|
+
instead of living indefinitely in a parent shell or plaintext project file.
|
|
117
|
+
Use `eldlock shell` when you want an interactive copy of your current shell
|
|
118
|
+
with the vault env already present. Eldlock unlocks the vault through the
|
|
119
|
+
daemon first, prints a success line, then starts a clean interactive instance
|
|
120
|
+
of your shell from the CLI process with an `(eldlock:<vault>)` prompt marker.
|
|
121
|
+
Type `exit` to leave it.
|
|
122
|
+
|
|
123
|
+
Manual local test builds can use a stub passkey provider:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
export ELDLOCK_STUB_PASSKEY="local-test-passkey-change-me"
|
|
127
|
+
npm run build:dev
|
|
128
|
+
source .eldlock/dev-env.sh
|
|
129
|
+
eldlock init
|
|
130
|
+
eldlock start
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
For YubiKey Bio / FIDO2 testing, install `libfido2`, then build and run with:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
export GOFLAGS="-tags=fido2"
|
|
137
|
+
export ELDLOCK_FIDO2_UV=true
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
If your key requires a PIN, the CLI prompts after the daemon reports that the
|
|
141
|
+
PIN is required.
|
|
142
|
+
|
|
143
|
+
## Philosophy
|
|
144
|
+
|
|
145
|
+
Eldlock's hard product principle:
|
|
146
|
+
|
|
147
|
+
> If adoption requires rewriting the app, changing every deploy process, or
|
|
148
|
+
> teaching every tool about Eldlock, the design has failed.
|
|
149
|
+
|
|
150
|
+
The daemon is the trust boundary. The CLI and TUI are frontends only: they may
|
|
151
|
+
request actions, show state, and launch flows, but security-critical behavior
|
|
152
|
+
belongs in `eldlock-server`.
|
|
153
|
+
|
|
154
|
+
That means policy decisions, approval state, caller inspection, encryption,
|
|
155
|
+
decryption, SSH signing, env injection, and audit logging must be enforced
|
|
156
|
+
daemon-side. A raw local socket client should not be able to bypass the rules by
|
|
157
|
+
skipping the CLI.
|
|
158
|
+
|
|
159
|
+
For integrations, Eldlock proxies the smallest useful operation:
|
|
160
|
+
|
|
161
|
+
| Secret type | Eldlock integration point |
|
|
162
|
+
| --- | --- |
|
|
163
|
+
| SSH keys | OpenSSH-compatible agent socket via `SSH_AUTH_SOCK` |
|
|
164
|
+
| Env vars | `eldlock exec -- <command>` |
|
|
165
|
+
| `.env` compatibility | ephemeral materialization first; stronger virtual-file approaches later |
|
|
166
|
+
| API tokens used by CLIs | wrapper, credential helper, plugin, or env injection |
|
|
167
|
+
|
|
168
|
+
Env vars cannot be intercepted after a process has already started. Eldlock can
|
|
169
|
+
launch a process with the required env vars, but it cannot make arbitrary env
|
|
170
|
+
reads magically lazy.
|
|
171
|
+
|
|
172
|
+
## Threat Model
|
|
173
|
+
|
|
174
|
+
Eldlock helps against passive or opportunistic secret theft:
|
|
175
|
+
|
|
176
|
+
- dependency install scripts scraping `.env`, `~/.ssh`, and common token files
|
|
177
|
+
- local token grabbers scanning developer directories
|
|
178
|
+
- accidental commits of `.env` files
|
|
179
|
+
- stolen laptops where the attacker only has disk access
|
|
180
|
+
- silent SSH private-key exfiltration
|
|
181
|
+
- tools that need standard env vars or normal OpenSSH agent behavior
|
|
182
|
+
|
|
183
|
+
Eldlock does not fully protect against:
|
|
184
|
+
|
|
185
|
+
- malware running as your user during an approved secret release
|
|
186
|
+
- malware replacing shell hooks or common binaries like `git`, `ssh`, or `npm`
|
|
187
|
+
- malware reading child process memory
|
|
188
|
+
- malware scraping injected env vars after `eldlock exec`
|
|
189
|
+
- malware using the SSH agent during an approval TTL window
|
|
190
|
+
- root-level compromise
|
|
191
|
+
|
|
192
|
+
Useful claim:
|
|
193
|
+
|
|
194
|
+
> Eldlock prevents secrets from sitting around passively stealable and requires
|
|
195
|
+
> local policy plus user presence before use.
|
|
196
|
+
|
|
197
|
+
Avoid claims like "impossible to steal" or "fully secure on a compromised
|
|
198
|
+
machine."
|
|
199
|
+
|
|
200
|
+
## Develop
|
|
201
|
+
|
|
202
|
+
Repository layout:
|
|
203
|
+
|
|
204
|
+
```text
|
|
205
|
+
eldlock-server/ Go daemon and security boundary
|
|
206
|
+
eldlock-cli/ TypeScript CLI/TUI frontend
|
|
207
|
+
docs/ architecture and threat model notes
|
|
208
|
+
examples/ example project configuration
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Common commands:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Build production artifacts into ./dist/eldlock
|
|
215
|
+
npm run build:prod
|
|
216
|
+
|
|
217
|
+
# Install eldlock for the whole machine at ~/.local/bin/eldlock
|
|
218
|
+
npm run install:local
|
|
219
|
+
|
|
220
|
+
# Local-only release build; requires an enrolled release YubiKey
|
|
221
|
+
npm run release
|
|
222
|
+
|
|
223
|
+
# First-time release YubiKey enrollment
|
|
224
|
+
npm run release -- --enroll
|
|
225
|
+
|
|
226
|
+
# Dry-run npm publish through eldlock_secrets/NODE_AUTH_TOKEN
|
|
227
|
+
npm run release -- --publish-dry-run
|
|
228
|
+
|
|
229
|
+
# Publish to npm through eldlock_secrets/NODE_AUTH_TOKEN
|
|
230
|
+
npm run release -- --publish
|
|
231
|
+
|
|
232
|
+
# Choose production install locations
|
|
233
|
+
node scripts/build-production.mjs \
|
|
234
|
+
--install-dir "$HOME/.local/share/eldlock" \
|
|
235
|
+
--bin-dir "$HOME/.local/bin"
|
|
236
|
+
|
|
237
|
+
# Server checks
|
|
238
|
+
cd eldlock-server
|
|
239
|
+
go run ./cmd/eldlock-server version
|
|
240
|
+
go test -mod=vendor ./...
|
|
241
|
+
|
|
242
|
+
# CLI checks
|
|
243
|
+
cd eldlock-cli
|
|
244
|
+
npm run typecheck
|
|
245
|
+
npm run build
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Vendored dependencies keep normal local development off live package registries:
|
|
249
|
+
|
|
250
|
+
- Go server dependencies live in `eldlock-server/vendor/`; server builds use
|
|
251
|
+
`-mod=vendor`.
|
|
252
|
+
- The patched `go-libfido2` binding lives in `eldlock-server/internal/libfido2/`.
|
|
253
|
+
- CLI npm tarballs live in `eldlock-cli/vendor/npm/`.
|
|
254
|
+
- `eldlock-cli/.npmrc` keeps npm offline for this package.
|
|
255
|
+
|
|
256
|
+
Npm releases are intentionally local-only. The package publishes as
|
|
257
|
+
`@reegaviljoen/eldlock` because npm rejected the unscoped `eldlock` name as too
|
|
258
|
+
similar to an existing package. Before publishing, install the
|
|
259
|
+
machine-wide command with `npm run install:local`, enroll the release YubiKey
|
|
260
|
+
with `npm run release -- --enroll`, then create an Eldlock vault named
|
|
261
|
+
`eldlock_secrets` containing the standard npm env secret `NODE_AUTH_TOKEN`.
|
|
262
|
+
The release script builds and verifies Eldlock locally, authenticates with the
|
|
263
|
+
release YubiKey, then runs normal `npm publish --access public` through
|
|
264
|
+
`eldlock exec --vault eldlock_secrets`. Use an npm automation token, or a
|
|
265
|
+
granular token with package publish access and 2FA bypass enabled, as
|
|
266
|
+
`NODE_AUTH_TOKEN`.
|
|
267
|
+
|
|
268
|
+
To verify env injection without printing the token, run:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
eldlock exec --vault eldlock_secrets -- 'test -n "$NODE_AUTH_TOKEN" && echo NODE_AUTH_TOKEN is set'
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
If the secret is not already set in your outer shell, quote shell variables in
|
|
275
|
+
one command string, such as `eldlock exec -- 'echo "$NODE_AUTH_TOKEN"'`.
|
|
276
|
+
Unquoted variables are expanded by your outer shell before Eldlock starts.
|
|
277
|
+
|
|
278
|
+
Storage note: the 0.1 preview uses an encrypted JSON envelope. Eldlock should
|
|
279
|
+
avoid SQL/SQLite for vault storage so SQL injection is not part of the storage
|
|
280
|
+
threat model.
|
|
281
|
+
|
|
282
|
+
The deeper docs are intentionally more detailed:
|
|
283
|
+
|
|
284
|
+
- [Architecture](docs/architecture.md)
|
|
285
|
+
- [Threat model](docs/threat-model.md)
|
package/bin/eldlock
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
3
|
+
if [ "${1:-}" = "" ] || [ "${1:-}" = "tui" ]; then
|
|
4
|
+
if [ -n "${ELDLOCK_TUI_RUNTIME:-}" ]; then
|
|
5
|
+
exec "$ELDLOCK_TUI_RUNTIME" "$SCRIPT_DIR/../eldlock-cli/dist/main.js" "$@"
|
|
6
|
+
fi
|
|
7
|
+
if command -v bun >/dev/null 2>&1; then
|
|
8
|
+
exec bun "$SCRIPT_DIR/../eldlock-cli/dist/main.js" "$@"
|
|
9
|
+
fi
|
|
10
|
+
fi
|
|
11
|
+
exec node "$SCRIPT_DIR/../eldlock-cli/dist/main.js" "$@"
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Eldlock Architecture
|
|
2
|
+
|
|
3
|
+
Eldlock is a local daemon plus a bundled CLI/TUI frontend.
|
|
4
|
+
|
|
5
|
+
The daemon owns all security-sensitive behavior:
|
|
6
|
+
|
|
7
|
+
- secret storage
|
|
8
|
+
- encryption and decryption
|
|
9
|
+
- YubiKey unlock or wrapping
|
|
10
|
+
- policy decisions
|
|
11
|
+
- approvals
|
|
12
|
+
- SSH signing
|
|
13
|
+
- env injection
|
|
14
|
+
- audit logging
|
|
15
|
+
|
|
16
|
+
The CLI/TUI is intentionally thin. It asks the daemon to do things; it does not decide whether those things are allowed.
|
|
17
|
+
|
|
18
|
+
## Components
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
┌────────────────────┐
|
|
22
|
+
│ eldlock-cli / TUI │
|
|
23
|
+
│ frontend only │
|
|
24
|
+
└─────────┬──────────┘
|
|
25
|
+
│
|
|
26
|
+
│ Unix socket API
|
|
27
|
+
v
|
|
28
|
+
┌──────────────────────────────────────────────────────┐
|
|
29
|
+
│ eldlock-server local daemon │
|
|
30
|
+
│ │
|
|
31
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │
|
|
32
|
+
│ │ policy │ │ approval │ │ audit log │ │
|
|
33
|
+
│ └──────┬───────┘ └──────┬───────┘ └──────┬──────┘ │
|
|
34
|
+
│ │ │ │ │
|
|
35
|
+
│ ┌──────v───────┐ ┌──────v───────┐ ┌──────v──────┐ │
|
|
36
|
+
│ │ crypto │ │ YubiKey │ │ caller ID │ │
|
|
37
|
+
│ └──────┬───────┘ └──────────────┘ └─────────────┘ │
|
|
38
|
+
│ │ │
|
|
39
|
+
│ ┌──────v─────────────────────────────────────────┐ │
|
|
40
|
+
│ │ local encrypted vault │ │
|
|
41
|
+
│ └────────────────────────────────────────────────┘ │
|
|
42
|
+
│ │
|
|
43
|
+
│ Interfaces: SSH agent, exec launcher, shell hooks │
|
|
44
|
+
└──────────────────────────────────────────────────────┘
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Local API
|
|
48
|
+
|
|
49
|
+
The daemon should listen on a local Unix domain socket, for example:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
$XDG_RUNTIME_DIR/eldlock/daemon.sock
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
or on macOS:
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
$HOME/Library/Application Support/Eldlock/daemon.sock
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The local API should never be treated as trusted just because it is local. The daemon must inspect the peer process and apply policy before releasing secrets or signing anything.
|
|
62
|
+
|
|
63
|
+
Important caller signals:
|
|
64
|
+
|
|
65
|
+
- UID/GID
|
|
66
|
+
- PID
|
|
67
|
+
- executable path
|
|
68
|
+
- command line
|
|
69
|
+
- cwd
|
|
70
|
+
- parent process
|
|
71
|
+
- attached TTY/session
|
|
72
|
+
- requested secret
|
|
73
|
+
- requested action
|
|
74
|
+
- approval TTL state
|
|
75
|
+
|
|
76
|
+
On Linux, the daemon can use Unix socket peer credentials such as `SO_PEERCRED`. macOS has different peer credential APIs and needs its own implementation path.
|
|
77
|
+
|
|
78
|
+
## SSH Agent
|
|
79
|
+
|
|
80
|
+
Eldlock should expose an OpenSSH-compatible agent socket:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
export SSH_AUTH_SOCK="$HOME/.eldlock/ssh-agent.sock"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Normal tools should keep working:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
ssh server
|
|
90
|
+
git clone git@github.com:org/repo.git
|
|
91
|
+
git pull
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The private key should not be exposed to the SSH client. The client asks the agent to sign a challenge. Eldlock checks policy, requires approval when needed, decrypts the key in memory, signs, and clears plaintext material as quickly as possible.
|
|
95
|
+
|
|
96
|
+
## Env Injection
|
|
97
|
+
|
|
98
|
+
Env vars cannot be lazily fetched after a process starts. Eldlock should launch the process with the required env vars:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
eldlock exec -- bin/dev
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Later, shell hooks can make this feel natural:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
bin/dev
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The shell hook can detect project config and route the command through `eldlock exec`.
|
|
111
|
+
|
|
112
|
+
## Storage
|
|
113
|
+
|
|
114
|
+
The 0.1 preview uses an encrypted JSON envelope stored on the local machine.
|
|
115
|
+
The envelope uses established primitives through the Go implementation, with
|
|
116
|
+
key material requested from the configured passkey provider for vault create,
|
|
117
|
+
read, add, list, and remove operations.
|
|
118
|
+
|
|
119
|
+
Eldlock should avoid SQL/SQLite for vault storage so SQL injection is not part
|
|
120
|
+
of the storage threat model. Do not invent custom crypto.
|
|
121
|
+
|
|
122
|
+
Conceptual record model:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
secrets
|
|
126
|
+
id
|
|
127
|
+
name
|
|
128
|
+
type
|
|
129
|
+
encrypted_blob
|
|
130
|
+
metadata_json
|
|
131
|
+
created_at
|
|
132
|
+
updated_at
|
|
133
|
+
|
|
134
|
+
policies
|
|
135
|
+
id
|
|
136
|
+
secret_id
|
|
137
|
+
allowed_cwd
|
|
138
|
+
allowed_process
|
|
139
|
+
approval_mode
|
|
140
|
+
ttl_seconds
|
|
141
|
+
require_touch
|
|
142
|
+
|
|
143
|
+
audit_events
|
|
144
|
+
id
|
|
145
|
+
event_type
|
|
146
|
+
secret_id
|
|
147
|
+
caller_pid
|
|
148
|
+
caller_exe
|
|
149
|
+
caller_cwd
|
|
150
|
+
decision
|
|
151
|
+
created_at
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Potential encryption model:
|
|
155
|
+
|
|
156
|
+
```text
|
|
157
|
+
YubiKey-backed wrapping provider
|
|
158
|
+
↓ unwraps
|
|
159
|
+
local master key
|
|
160
|
+
↓ decrypts
|
|
161
|
+
per-secret encryption keys or secret blobs
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The YubiKey must be part of unwrapping key material, not just a cosmetic approval check.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Threat Model
|
|
2
|
+
|
|
3
|
+
Eldlock is not a magic shield for compromised machines. It is designed to reduce passive secret theft and make secret use require local policy plus user presence.
|
|
4
|
+
|
|
5
|
+
## Protects Against
|
|
6
|
+
|
|
7
|
+
Eldlock should materially help against:
|
|
8
|
+
|
|
9
|
+
- dependency install scripts scraping `~/.ssh`, `.env`, and common token files
|
|
10
|
+
- local token grabbers scanning developer directories
|
|
11
|
+
- accidental commits of `.env` files
|
|
12
|
+
- stolen laptops where the attacker only has disk access
|
|
13
|
+
- silent theft of SSH private keys
|
|
14
|
+
- tools that need standard env vars or normal OpenSSH agent behavior
|
|
15
|
+
|
|
16
|
+
## Does Not Fully Protect Against
|
|
17
|
+
|
|
18
|
+
Eldlock cannot fully protect against:
|
|
19
|
+
|
|
20
|
+
- malware running as the user during an approved secret release
|
|
21
|
+
- malware modifying shell hooks
|
|
22
|
+
- malware replacing binaries like `git`, `ssh`, `rails`, or `npm`
|
|
23
|
+
- malware reading child process memory
|
|
24
|
+
- malware scraping environment variables after `eldlock exec`
|
|
25
|
+
- malware using the SSH agent during an approval TTL window
|
|
26
|
+
- root-level compromise
|
|
27
|
+
|
|
28
|
+
## Design Implication
|
|
29
|
+
|
|
30
|
+
The daemon should assume local callers may be hostile. A command like this must not be automatically safe:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
eldlock secret read personal/openai/api_key
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Every request should go through policy checks, caller inspection, approval, and audit logging.
|
|
37
|
+
|
|
38
|
+
## Useful Claim
|
|
39
|
+
|
|
40
|
+
Good:
|
|
41
|
+
|
|
42
|
+
> Eldlock prevents secrets from sitting around passively stealable and requires local policy plus user presence before use.
|
|
43
|
+
|
|
44
|
+
Bad:
|
|
45
|
+
|
|
46
|
+
> Eldlock makes secrets impossible to steal.
|
|
47
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# eldlock-cli
|
|
2
|
+
|
|
3
|
+
`eldlock-cli` is the bundled command-line and TUI frontend for Eldlock.
|
|
4
|
+
|
|
5
|
+
It is intentionally not the trust boundary. It talks to the local `eldlock-server` daemon over a Unix socket.
|
|
6
|
+
|
|
7
|
+
Security-critical logic belongs in `eldlock-server`, including:
|
|
8
|
+
|
|
9
|
+
- policy decisions
|
|
10
|
+
- encryption and decryption
|
|
11
|
+
- YubiKey unlock
|
|
12
|
+
- SSH signing
|
|
13
|
+
- env injection
|
|
14
|
+
- audit logging
|
|
15
|
+
|
|
16
|
+
## TUI Direction
|
|
17
|
+
|
|
18
|
+
The intended TUI direction is OpenTUI-style: a rich terminal frontend similar in feel to OpenCode.
|
|
19
|
+
|
|
20
|
+
The TUI should render state and collect user intent. The daemon decides what is allowed.
|
|
21
|
+
|
|
22
|
+
## Prototype Secret Commands
|
|
23
|
+
|
|
24
|
+
Initialize the vault:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
export ELDLOCK_STUB_PASSKEY="local-test-passkey-change-me"
|
|
28
|
+
npm run dev -- init
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Start the daemon:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
export ELDLOCK_SERVER_PATH="../eldlock-server/bin/eldlock-server-dev"
|
|
35
|
+
npm run dev -- start
|
|
36
|
+
npm run dev -- status
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The public CLI does not require Go or mise at runtime. `eldlock start` launches the binary at `ELDLOCK_SERVER_PATH`, or finds `eldlock-server` on `PATH`.
|
|
40
|
+
|
|
41
|
+
In another terminal, add and read an env secret:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm run dev -- secret add demo/API_KEY=test-secret
|
|
45
|
+
npm run dev -- secret read demo/API_KEY
|
|
46
|
+
npm run dev -- secret read demo/API_KEY --plain
|
|
47
|
+
npm run dev -- secret remove demo/API_KEY
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Stop the daemon:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm run dev -- stop
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The CLI sends requests to the daemon over the Unix socket. Clipboard writes happen in the daemon, not in TypeScript.
|