dotlock-bin 0.1.5

Encrypted project-local environment variables manager
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
<div align="center">

```
    ██████╗   ██████╗  ████████╗ ██╗       ██████╗   ██████╗ ██╗  ██╗
    ██╔══██╗ ██╔═══██╗ ╚══██╔══╝ ██║      ██╔═══██╗ ██╔════╝ ██║ ██╔╝
    ██║  ██║ ██║   ██║    ██║    ██║      ██║   ██║ ██║      █████╔╝
    ██║  ██║ ██║   ██║    ██║    ██║      ██║   ██║ ██║      ██╔═██╗
    ██████╔╝ ╚██████╔╝    ██║    ███████╗ ╚██████╔╝ ╚██████╗ ██║  ██╗
    ╚═════╝   ╚═════╝     ╚═╝    ╚══════╝  ╚═════╝   ╚═════╝ ╚═╝  ╚═╝
```

### Encrypt your `.env`. Share by key, not by password.

[![Made with Rust](https://img.shields.io/badge/Made%20with-Rust-DEA584?style=for-the-badge&logo=rust&logoColor=white)](https://www.rust-lang.org/)
[![Cipher](https://img.shields.io/badge/Cipher-XChaCha20--Poly1305-FF1744?style=for-the-badge)](https://en.wikipedia.org/wiki/ChaCha20-Poly1305)
[![KDF](https://img.shields.io/badge/KDF-Argon2id-7C4DFF?style=for-the-badge)](https://en.wikipedia.org/wiki/Argon2)
[![Sharing](https://img.shields.io/badge/Sharing-RSA--OAEP-00BFA5?style=for-the-badge)](https://en.wikipedia.org/wiki/Optimal_asymmetric_encryption_padding)
[![Status](https://img.shields.io/badge/Status-Alpha-FF9800?style=for-the-badge)](#status)

</div>

---

## Description

DotLock is a local, file-based secrets manager for project environment variables. It stores secrets in a `.lock/` vault next to your code and injects decrypted values only into child processes launched with `dl run`.

Use DotLock when you want:

- to keep `.env` values encrypted in a repository;
- to run local commands with decrypted variables without writing plaintext back to disk;
- to share project secrets with teammates through public keys instead of a shared password;
- to keep a lightweight audit log of unlocks, runs, key ratchets and dynamic provider resolutions.

DotLock is not a hosted production secret manager. For large production systems, prefer a dedicated service such as AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault or your platform's native secret store.

## Status

DotLock is alpha software. The cryptographic building blocks are conservative, but the on-disk format and CLI surface may still change before `1.0`. Keep backups of `.lock/` and review changes before upgrading across `0.1.x` releases.

## Installation

### From crates.io

```bash
cargo install dotlock-bin
```

The crate is named `dotlock-bin`; the installed binary is named `dl`.

### From source

```bash
cargo build --release
install -m 0755 target/release/dl ~/.local/bin/dl
```

DotLock uses Rust edition 2024.

## Quick Start

```bash
cd my-project
dl init
dl migrate .env
dl run npm start
```

What happens:

1. `dl init` creates `.lock/vault.toml` and `.lock/secrets.lock`.
2. `dl migrate .env` imports variables from an existing `.env` file.
3. `dl run npm start` starts `npm start` with decrypted secrets in the child process environment.

After migration, remove the plaintext `.env` only when you have verified the imported values:

```bash
dl list
dl get DATABASE_URL
rm .env
```

## Core Workflow

### Initialize a project

**What:** `dl init` creates the project vault in the current directory.

**How:**

```bash
dl init
```

You will choose whether DotLock generates a strong master password or lets you type one. The vault metadata goes into `.lock/vault.toml`; encrypted secret records go into `.lock/secrets.lock`.

**When:** Run once per project before using any other project command.

### Add or update a static secret

**What:** `dl set NAME VALUE` stores a secret value under a normalized environment variable name.

**How:**

```bash
dl set DATABASE_URL "postgres://localhost/app_dev"
dl set STRIPE_KEY "sk_live_..."
dl set API_KEY "secret" --alg xcha
```

Aliases:

```bash
dl s DATABASE_URL "postgres://localhost/app_dev"
dl add DATABASE_URL "postgres://localhost/app_dev"
```

Names must use letters, digits and underscores, and cannot start with a digit. DotLock normalizes names to uppercase.

`--alg` (short `-a`) selects the encryption algorithm for static secrets. The current supported value is `xcha`, which maps to XChaCha20-Poly1305.

**When:** Use this when adding a new secret or replacing an existing value.

### Read a secret

**What:** `dl get NAME` decrypts one secret and prints it.

**How:**

```bash
dl get DATABASE_URL
dl get DATABASE_URL | pbcopy
```

When stdout is a terminal, DotLock renders a boxed display with the secret name and short id. When stdout is piped, it prints the raw value.

**When:** Use sparingly for copying or debugging a single value. Prefer `dl run` for normal command execution.

### List secrets

**What:** `dl list` shows stored secret names and short ids without plaintext values.

**How:**

```bash
dl list
dl l
```

**When:** Use before `get`, `unset`, ACL changes or audits to confirm what is stored.

### Remove a secret

**What:** `dl unset NAME` removes a secret and its access wrapping metadata.

**How:**

```bash
dl unset OLD_FEATURE_FLAG
dl rm OLD_FEATURE_FLAG
```

Other aliases: `remove`, `u`, `d`, `del`, `delete`.

**When:** Use when a variable is no longer needed or should no longer be available to `dl run`.

### Run a command with secrets

**What:** `dl run CMD [args...]` decrypts accessible secrets and starts a child process with those values in its environment.

**How:**

```bash
dl run npm start
dl run python manage.py runserver
dl r cargo test
```

The `--` separator is important when the command has its own flags.

**When:** Use this as the normal way to consume secrets locally or in scripts.

### Lock the session

**What:** `dl lock` deletes the cached project key for the current project.

**How:**

```bash
dl lock
dl logout
```

**When:** Use after a sensitive session, before handing off a machine, or at the end of CI/scripted workflows.

## Import and Export `.env` Files

### Import from `.env`

**What:** `dl migrate [PATH]` imports variables from a `.env` file into the encrypted vault.

**How:**

```bash
dl migrate
dl migrate .env.local
dl import .env.production
```

The parser accepts:

- `KEY=VALUE`;
- `export KEY=VALUE`;
- single-quoted values;
- double-quoted values with escapes such as `\n`, `\r`, `\t`, `\\` and `\"`;
- comments and blank lines.

**When:** Use when adopting DotLock in an existing project or bulk-loading variables.

### Export to `.env`

**What:** `dl export [PATH]` writes missing static secrets into a `.env` file.

**How:**

```bash
dl export
dl export .env.local
```

Existing keys are never overwritten. Dynamic secrets are skipped because their values are resolved at runtime.

**When:** Use for tools that still require a `.env` file, or when bridging DotLock with legacy local workflows.

## Team Sharing

DotLock supports shared access through a local RSA identity. The project owner keeps the master password; recipients unlock with their own private key.

### Create a local identity

**What:** `dl cert init` creates a local RSA key pair.

**How:**

```bash
dl cert init
dl cert init --plain
dl cert init --force
```

Aliases:

```bash
dl crt init
dl crt i
```

By default, the private key is passphrase-protected. `--plain` creates an unencrypted private key. `--force` replaces an existing identity.

**When:** Each developer or CI environment should do this once before requesting shared access.

### Show or export the public key

**What:** `dl cert show` prints the local identity fingerprint and paths. `dl cert export-pub [PATH]` prints or writes the public key.

**How:**

```bash
dl cert show
dl cert export-pub alice.pub
dl cert export-pub
```

Aliases: `dl crt sh`, `dl crt x`.

**When:** Use `show` to confirm which identity is active. Use `export-pub` when sending a public key to a vault owner.

### Enable shared mode

**What:** `dl share enable` marks the vault as shared.

**How:**

```bash
dl share enable
dl shr en
```

**When:** Use before granting access to teammates or CI.

### Grant access

**What:** `dl share grant` adds or updates a recipient.

**How:**

```bash
dl share grant --pubkey alice.pub --label alice
dl share grant --pubkey ci.pub --label ci --allow DATABASE_URL,REDIS_URL
```

Aliases: `dl shr gr`.

Without `--allow`, the recipient gets full access. With `--allow`, the recipient can decrypt only the listed secrets.

**When:** Use when onboarding a developer, adding a CI identity, or changing a recipient's access scope.

### Manage a recipient ACL

**What:** `dl share allow` lists, adds or removes per-secret access for a recipient.

**How:**

```bash
dl share allow ci --list
dl share allow ci --add STRIPE_KEY
dl share allow ci --remove REDIS_URL
```

The recipient query can be the recipient id, label or public key fingerprint. Removing a secret from a recipient rotates that secret's SDK so the old recipient wrapping cannot decrypt future values.

**When:** Use for least-privilege access, especially for CI identities.

### List or revoke recipients

**What:** `dl share list` shows recipients. `dl share revoke QUERY` removes a recipient and rotates project access material.

**How:**

```bash
dl share list
dl share revoke alice
```

Aliases: `dl shr l`, `dl shr rev`.

**When:** Use `list` before access reviews. Use `revoke` when someone leaves the project or an identity is compromised.

## Rotation and Ratcheting

### Rotate the master password

**What:** `dl rotate master-password` changes the password used to unlock the project key.

**How:**

```bash
dl rotate master-password
dl rotate mp
```

**When:** Use when a password may have been exposed but the encrypted vault itself does not need a full project-key rotation.

### Rotate key wrapping material

**What:** `dl rotate kek` rotates the key wrapping layer and rewraps secret SDKs and full-access recipient project keys.

**How:**

```bash
dl rotate kek
```

**When:** Use as hygiene after many writes, or let DotLock trigger it automatically with `auto_ratchet_after_writes`.

### Rotate the project key

**What:** `dl rotate project-key` generates a new project key and rewrites encrypted secret records.

**How:**

```bash
dl rotate project-key
dl rotate pk
```

**When:** Use after a suspected project-key exposure or after major access changes that justify a heavier rotation.

## Dynamic Secret Providers

Dynamic secrets are resolved by external executables instead of storing a fixed plaintext value. Provider binaries must be named `dotlock-provider-NAME` and be available on `PATH`.

### Discover providers

**What:** `dl provider list` lists provider binaries found on `PATH`. `dl provider info NAME` runs a provider's `--describe` command.

**How:**

```bash
dl provider list
dl provider info aws
dl p list
dl p info aws
```

**When:** Use before creating a dynamic secret, or when debugging provider installation.

### Store a dynamic secret

**What:** `dl set NAME --provider PROVIDER` stores encrypted provider metadata instead of a static value.

**How:**

```bash
dl set DATABASE_URL --provider aws --config '{"name":"prod/db/url"}'
dl set API_TOKEN --provider vault --config '{"path":"secret/api"}' --bootstrap VAULT_TOKEN
```

`--config` must be valid JSON. `--bootstrap` is a comma-separated list of existing static secrets that DotLock decrypts and passes to the provider at resolve time.

**When:** Use when the value should be minted or fetched just-in-time from another local provider, while DotLock still controls bootstrap material and runtime injection.

### Provider protocol

Providers are executed as child processes:

- `dotlock-provider-NAME --describe` prints human-readable provider information.
- `dotlock-provider-NAME --resolve` reads JSON on stdin and prints the resolved secret value on stdout.

The resolve input has this shape:

```json
{
  "config": {},
  "bootstrap": {
    "VAULT_TOKEN": "decrypted bootstrap value"
  }
}
```

DotLock records the provider path and SHA-256 digest when the dynamic secret is created. Later resolutions verify the digest before running the provider. Provider stdout is limited to 64 KiB and stderr to 16 KiB.

## Git Integration

### Install the merge driver

**What:** `dl git install-merge-driver` configures Git to use DotLock's merge driver for `.lock/secrets.lock` and `.lock/vault.toml`.

**How:**

```bash
dl git install-merge-driver
```

`dl init` also attempts to install the merge driver when the project is inside a Git work tree.

**When:** Use in repositories where multiple branches or teammates may edit the vault.

### Auto-fetch before run

**What:** when enabled, `dl run` tries a short `git pull --ff-only --no-rebase REMOTE BRANCH` before unlocking.

**How:**

```bash
dl config set auto_fetch_on_run true
dl config set auto_fetch_timeout_secs 3
dl config set auto_fetch_remote origin

DOTLOCK_AUTO_FETCH=0 dl run npm test
```

If pull fails, DotLock tries `git fetch` and then continues with the local vault.

**When:** Use when teams commit `.lock/` changes frequently and want `dl run` to pick up fast-forward updates automatically.

## Audit Log

DotLock keeps a local audit log outside the project by default. Entries are hash-chained. If a local plain identity is available, entries are signed; otherwise they are anonymous.

### Show audit entries

**What:** `dl audit show` prints audit entries.

**How:**

```bash
dl audit show
dl audit show --verbose
dl audit show --since 2026-05-01
dl audit show --action run
```

Aliases: `dl audit s`, `dl a show`.

**When:** Use during local review, debugging or incident response.

### Verify or rotate logs

**What:** `dl audit verify` checks the hash chain and signatures. `dl audit rotate` compresses the current log with `gzip`. `dl audit path` prints the current log path.

**How:**

```bash
dl audit verify
dl audit verify --strict
dl audit path
dl audit rotate
```

`--strict` rejects anonymous entries. Logs rotate automatically when they reach 10 MiB or are older than 90 days.

**When:** Use `verify` before trusting audit history. Use `rotate` for manual cleanup or archival.

## Configuration

### Project configuration

Project configuration is stored in `.lock/vault.toml`.

```bash
dl config show
dl config set auto_fetch_on_run true
dl config set auto_fetch_timeout_secs 3
dl config set auto_fetch_remote origin
dl config set auto_ratchet_after_writes 50
dl config set dynamic_resolve_timeout_secs 10
dl config unset auto_fetch_remote
```

Aliases: `dl c show`, `dl c set`, `dl c unset`.

| Key | Default | Accepted values | Effect |
|---|---:|---|---|
| `auto_fetch_on_run` | `false` | `true`, `false`, `1`, `0`, `yes`, `no`, `on`, `off` | Enables Git auto-fetch before `dl run` |
| `auto_fetch_timeout_secs` | `3` | positive integer | Timeout for Git auto-fetch operations |
| `auto_fetch_remote` | `origin` | non-empty string | Remote used by auto-fetch |
| `auto_ratchet_after_writes` | off | non-negative integer | Automatically rotates key wrapping after this many vault writes; `0` disables it |
| `dynamic_resolve_timeout_secs` | `10` | positive integer | Timeout for dynamic provider resolution |

### Environment variables

| Variable | Default | Effect |
|---|---|---|
| `DOTLOCK_CACHE_TTL` | `30` | Cached project key lifetime in seconds, capped at 3600 |
| `DOTLOCK_CACHE_DIR` | `$HOME/.lock` on Unix, `%LOCALAPPDATA%\dotlock` on Windows | Overrides the session cache root |
| `DOTLOCK_SHARED_CACHE` | off | Set to `1`, `true`, `TRUE`, `yes` or `YES` to enable caching in shared mode |
| `DOTLOCK_IDENTITY_DIR` | `$HOME/.lock/identity` | Overrides local identity storage |
| `DOTLOCK_AUDIT_DIR` | `$HOME/.lock/audit` on Unix, `%LOCALAPPDATA%\dotlock\audit` on Windows | Overrides audit log storage |
| `DOTLOCK_AUTO_FETCH` | unset | Set to `0`, `false`, `no` or `off` to disable auto-fetch for one command |

## Command Reference

| Command | Aliases | Purpose |
|---|---|---|
| `dl init` | `i` | Initialize `.lock/` in the current project |
| `dl set [--alg xcha] NAME VALUE` | `s`, `add` | Store or update a static secret |
| `dl set NAME --provider PROVIDER [--config JSON] [--bootstrap A,B]` | `s`, `add` | Store a dynamic secret definition |
| `dl get NAME` | `g` | Print a secret value |
| `dl unset NAME` | `rm`, `remove`, `u`, `d`, `del`, `delete` | Remove a secret |
| `dl list` | `l` | List stored secrets without plaintext |
| `dl run CMD [args...]` | `r` | Run a command with decrypted secrets in its environment |
| `dl lock` | `logout` | Drop the cached project key |
| `dl migrate [PATH]` | `m`, `import` | Import variables from `.env` |
| `dl export [PATH]` | `x` | Append missing static secrets to `.env` |
| `dl cert init [--force] [--plain]` | `crt init`, `crt i` | Create a local RSA identity |
| `dl cert show` | `crt show`, `crt sh` | Show local identity information |
| `dl cert export-pub [PATH]` | `crt export-pub`, `crt x` | Print or write the public key |
| `dl share enable` | `shr enable`, `shr en` | Enable shared mode |
| `dl share grant --pubkey FILE --label LABEL [--allow A,B]` | `shr grant`, `shr gr` | Grant recipient access |
| `dl share allow QUERY --list` | none | List recipient ACL |
| `dl share allow QUERY --add A,B` | none | Add secrets to recipient ACL |
| `dl share allow QUERY --remove A,B` | none | Remove secrets from recipient ACL and rotate affected SDKs |
| `dl share revoke QUERY` | `shr revoke`, `shr rev` | Revoke a recipient |
| `dl share list` | `shr list`, `shr l` | List recipients |
| `dl rotate kek` | none | Rotate key wrapping material |
| `dl rotate master-password` | `rotate mp` | Change master password |
| `dl rotate project-key` | `rotate pk` | Rotate project key |
| `dl audit show [--verbose] [--since YYYY-MM-DD] [--action ACTION]` | `a show`, `audit s` | Show audit entries |
| `dl audit verify [--strict]` | `a verify`, `audit v` | Verify audit log |
| `dl audit path` | `a path` | Print audit log path |
| `dl audit rotate` | `a rotate` | Rotate and gzip current audit log |
| `dl git install-merge-driver` | none | Configure Git merge support |
| `dl config show` | `c show` | Show project config |
| `dl config set KEY VALUE` | `c set` | Set project config |
| `dl config unset KEY` | `c unset` | Reset project config |
| `dl provider list` | `p list` | List provider binaries on `PATH` |
| `dl provider info NAME` | `p info` | Show provider description |

## How It Works

### Files on disk

Project vault:

```text
.lock/
├── vault.toml
└── secrets.lock
```

Local user state:

```text
~/.lock/
├── identity/
│   ├── identity.pem
│   ├── identity.pub.pem
│   └── identity.toml
├── run/sessions/<project>/
│   └── sessions.toml
└── audit/<project-uuid>/
    └── audit.log
```

On Unix, private directories are created with `0700`, secret/private files with `0600`, and public key files with `0644`. Writes use an atomic temp-file-and-rename flow.

### Key hierarchy

```text
master password
  -> Argon2id derives master key
    -> HKDF derives KEK
      -> KEK wraps project key
        -> project key wraps per-secret SDKs
          -> SDK encrypts each secret value or dynamic provider metadata
```

Terms:

- Master password: typed or generated during `dl init`.
- Master key: derived from the password and salt; held briefly and zeroized.
- KEK: key-encryption key derived for wrapping the project key.
- Project key: random 32-byte key stored only in wrapped form.
- SDK: per-secret data key used to encrypt one secret's stored data.

This is why changing a master password is fast, while project-key and ACL rotations touch more vault metadata.

### Integrity

Every write to `secrets.lock` updates an authenticated hash in `vault.toml`. On unlock, DotLock verifies the hash before decrypting secrets. If `secrets.lock` was modified outside DotLock, the command aborts with a tamper error.

### Session cache

After successful unlock, DotLock may cache the project key for a short time. The default TTL is 30 seconds. Shared mode disables caching unless `DOTLOCK_SHARED_CACHE` is enabled.

### Shared mode and ACLs

Full-access recipients receive wrapped access to the project key and per-secret SDKs. Limited recipients receive only wrapped SDKs for allowed secrets. Removing a secret from a recipient ACL rotates the affected SDKs.

## Security Notes

- `dl get` prints plaintext. Prefer `dl run` when a command can consume secrets directly.
- Do not commit plaintext `.env` files after migrating to DotLock.
- Commit `.lock/vault.toml` and `.lock/secrets.lock` if your team shares the encrypted vault through Git.
- Keep local identity private keys protected. Use `dl cert init --plain` only for controlled automation or ephemeral environments.
- Dynamic providers are executable code. Install them from trusted sources and keep provider directories non-world-writable.
- Audit logs are local by default. They are useful for local accountability and debugging, not a centralized compliance system.

## License

Dual-licensed under your choice of:

- MIT: [LICENSE-MIT]./LICENSE-MIT
- Apache 2.0: [LICENSE-APACHE]./LICENSE-APACHE