Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions docs/env.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Planned direction:
- Portable env values will live in an immutable encrypted bundle artifact.
- Bundle access will be controlled through explicit project-key sharing.
- Applying a bundle will still write values back into `.hack/.env` and the configured local secret backend so existing runtime behavior stays intact.
- Key lifecycle operations will stay split into value rotation, share rotation, and project-key rotation so operators can change trust without accidentally rewriting secrets.
- Portable env projects will need an explicit recovery path before the last owner or recovery share can be removed.

See `docs/plans/2026-03-13-env-portability-and-secret-management-design.md` for the full portable env and key-sharing model.

Expand Down Expand Up @@ -166,6 +168,111 @@ This behavior is design intent for the managed artifact workflow; it is not full

See [docs/plans/2026-03-13-portable-project-env-artifact-schema-design.md](plans/2026-03-13-portable-project-env-artifact-schema-design.md) for the full design rationale and implementation boundary.

## Planned portable key lifecycle flows

Portable env management needs distinct operator flows because the actor intent is different in each case:

| Operator goal | Planned flow | Expected crypto effect | Expected operator impact |
| --- | --- | --- | --- |
| Change one or more env values | Value rotation | New immutable bundle version and new bundle key | Recipients keep the same project key access unless the operator also changes sharing |
| Add or remove recipients | Share rotation | Update wrapped project-key shares only | Existing bundle contents stay unchanged |
| Recover from suspected key compromise or re-anchor trust | Project-key rotation | Mint a new project key and re-wrap active bundle keys | Every active recipient share must be regenerated and reapproved |

### Value rotation

Use value rotation when the secret itself changed, such as a new API token or database password.

Planned rules:

- publish a new bundle version instead of mutating an old one
- mint a fresh bundle key for the new version
- keep project-key lineage stable unless the operator separately requests project-key rotation
- preserve audit metadata linking the new bundle to the superseded version

### Share rotation

Use share rotation when the set of people or teams with access changed, but the underlying values do not need to change.

Planned rules:

- add or revoke recipient-specific key-share records
- do not rewrite bundle ciphertext just because membership changed
- do not require republishing values unless the operator explicitly wants a clean post-change snapshot
- treat revocation as preventing fresh unwrap operations, not as proof that an old local copy was erased

### Project-key rotation

Use project-key rotation when the project key may be compromised, when a prior owner device is no longer trusted, or when the owner wants a stronger trust reset than share updates alone provide.

Planned rules:

- mint a new project key with a new durable key identifier
- re-wrap the latest active bundle keys to that new project key
- require every still-valid recipient share to be recreated against the new project key
- record lineage so audit logs can explain which key superseded which predecessor

## Recovery and lost-key handling

Hack should not rely on silent plaintext escrow. Recovery must stay explicit, operator-visible, and auditable.

### Required recovery guardrail

Before portable env custody can become the only authority, there must be at least one recovery path:

- an encrypted recovery package exported by the owner
- a second owner or admin share
- a designated recovery recipient share created on purpose

If no recovery path remains, Hack should block destructive actions unless the operator uses an explicit forced override.

### Lost-key situations

#### 1. Lost local machine, but another trusted recovery path exists

Planned recovery behavior:

- authenticate as the same Hack owner or another authorized recovery holder
- unwrap the active project key through the remaining recovery path
- re-apply the portable bundle into fresh local compatibility storage
- mint replacement recipient shares for any devices or operators that should keep access

This is a recovery event, not a value rotation event. The secrets stay the same unless compromise is suspected.

#### 2. A recipient loses their local key material

Planned recovery behavior:

- leave bundle versions unchanged
- reissue that recipient's wrapped project-key share after authorization checks
- require a full project-key rotation only if the old recipient device is considered untrusted or compromised

#### 3. The project key may be compromised

Planned recovery behavior:

- stop treating simple share reissue as sufficient
- require project-key rotation
- regenerate all active recipient and recovery shares from the new key
- preserve old-key lineage in audit history

#### 4. The final recovery path would be removed

Planned handling:

- reject deleting the last owner or recovery share during normal operations
- reject project-key rotation that would leave no valid recipient or recovery package
- require a loud, explicit forced override for destructive break-glass actions and emit an audit event

### UX expectations

The eventual CLI and desktop UX should explain which state the operator is in:

- `Rotate values`: secret contents changed
- `Update access`: recipients changed
- `Rotate project key`: trust anchor changed or may be compromised
- `Recover access`: local key material was lost but a valid recovery path still exists
- `Reconnect or recreate secrets`: no valid recovery path remains, so operator action must happen outside the portable recovery flow

## CLI

- `hack env list [--json] [--show-secrets]`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,17 @@ Behavior:
- require explicit regeneration of all active key-share records
- keep prior project-key lineage in audit metadata

### Operator decision matrix

The CLI and broker should treat these as different intents, not synonyms:

| Situation | Correct action | Why |
| --- | --- | --- |
| The underlying secret changed | Value rotation | The trust anchor can stay the same while publishing a new immutable value snapshot |
| A teammate left or a new teammate joined | Share rotation | Membership changed, but the current project key may still be trusted |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Treat teammate offboarding as requiring secret rotation

In the offboarding case where a teammate has already applied the current bundle, Share rotation is not a sufficient “correct action”: the sharing rules in docs/plans/2026-03-13-env-portability-and-secret-management-design.md:246-249 explicitly say revocation only blocks fresh unwraps and does not remove old local materializations. As written, this matrix will push the CLI/broker toward a flow that leaves the departed user with still-valid DB/API credentials until operators also rotate the affected secret values.

Useful? React with 👍 / 👎.

| A device was stolen, an owner key may be compromised, or the operator wants a hard trust reset | Project-key rotation | The trust anchor itself must change |
| A user lost local key material but another valid recovery path remains | Recovery / share reissue | Access must be restored without forcing unnecessary secret churn |

### Recovery policy

Hack should not silently escrow plaintext secrets. Recovery must be explicit.
Expand All @@ -305,6 +316,67 @@ Unsafe behavior to reject:
- rotating the project key while leaving no valid recipient or backup
- revoking the last usable recovery share without an explicit forced override

### Lost-key handling flows

The design needs to distinguish between `lost access` and `compromised access`.

#### Case 1: Lost local machine, trusted recovery still exists

Expected flow:

1. authenticate as an authorized owner or designated recovery recipient
2. unwrap the current project key through the remaining recovery path
3. re-apply the latest active portable bundle to new local compatibility storage
4. reissue recipient shares for replacement devices as needed
Comment on lines +327 to +330

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require owner/admin approval before recovery holders re-share

When the surviving authority is a designated recovery recipient, this Case 1 flow lets that backup-only identity complete step 4 and mint fresh recipient shares. That effectively promotes recovery holders into share administrators, even though manual sharing is described as the trust boundary earlier in this doc (docs/plans/2026-03-13-env-portability-and-secret-management-design.md:219-251). In a lost-laptop recovery, a designated recovery recipient could re-grant access to any user or device unless the reissue step is limited to owners/admins or requires explicit re-approval.

Useful? React with 👍 / 👎.


This should be logged as recovery, not as key compromise. Values and bundle history stay intact.

#### Case 2: Recipient lost local key material

Expected flow:

1. verify that the recipient is still entitled to access
2. mint a fresh wrapped project-key share for that recipient
3. preserve existing bundle versions and project-key lineage
Comment on lines +338 to +340

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bind share repair to recipient key material

Case 2 assumes we can "mint a fresh wrapped project-key share for that recipient", but the only key-share schema defined here records recipient.id plus wrapMethod/wrappedProjectKey and nothing about which public key or device the share targets (docs/plans/2026-03-13-env-portability-and-secret-management-design.md:223-240). In the normal lost-key scenario where the user creates a replacement keypair, the broker has no stable key identity to encrypt to or supersede, so this recovery flow cannot be implemented reliably without adding recipient key metadata.

Useful? React with 👍 / 👎.


This is basically share repair. It should not force value rotation or project-key rotation on its own.

#### Case 3: Owner or project key suspected compromised

Expected flow:

1. rotate the project key
2. re-wrap the latest active bundle keys to the new project key

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Re-wrap every retained bundle version after key compromise

Re-wrapping only the “latest active bundle keys” is not enough for a compromised-project-key scenario, because the design elsewhere keeps bundle versions immutable and fetchable by version (docs/plans/2026-03-06-remote-encrypted-project-env-portability-plan.md:158-160). Any older bundle that remains wrapped by the superseded project key is still decryptable by the attacker, so compromise rotation would leave historical secret snapshots exposed and still applicable. This flow needs to cover all retained bundle versions or explicitly retire old history.

Useful? React with 👍 / 👎.

3. regenerate all active recipient and recovery shares

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Regenerate recovery packages during project-key rotation

This compromise-handling flow rotates the project key and recreates shares, but it never reissues encrypted recovery packages. That breaks the case where a recovery package is the only remaining recovery path, because the new section later ties packages to a specific project key identifier in docs/plans/2026-03-13-env-portability-and-secret-management-design.md:367-376; after rotation, the old package can no longer unwrap the new key even though the guardrails still count a package as valid recovery. The rotation flow needs to explicitly regenerate or invalidate recovery packages to avoid silently stranding the project.

Useful? React with 👍 / 👎.

4. record the superseded key lineage and reason in audit metadata

If an operator cannot prove which recipient or device was affected, assume compromise and require project-key rotation instead of share repair.

#### Case 4: No valid recovery path remains

Expected flow:

1. block normal revoke or rotate actions that would strand the project
2. require explicit break-glass override semantics for destructive changes
3. present a user-facing message that portable recovery is no longer possible without recreating authority

The system should fail closed here. Hack must not pretend that remote recovery is available when no wrapped recovery authority exists.

### Recovery package expectations

An encrypted recovery package is an allowed recovery path, but it must stay metadata-rich enough to be usable later.

Minimum metadata:

- project binding identifier
- project key identifier
- creation timestamp
- creating actor
- encryption method / wrapping scheme identifier
- optional human description supplied by the operator

The package must not be treated as silent escrow. Operators should create it deliberately, store it deliberately, and understand that losing every recovery package plus every valid owner share means portable recovery is no longer possible.

## `.env` Compatibility And Backend UX

Portability should preserve the current local runtime model instead of replacing it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,14 +194,15 @@ Add coverage for:
- recovery package export metadata
- preventing deletion of the last recovery path
- preventing revocation of the last owner share without force
- lost-key recovery versus compromised-key rotation handling

**Step 2: Run test to verify it fails**

Run: `bun test --cwd services/auth-broker tests/index.test.ts && bun test tests/env-command.test.ts` Expected: FAIL because rotation and recovery guardrails are not yet implemented.

**Step 3: Write minimal implementation**

Implement separate commands for value rotation, share changes, and project-key rotation, plus explicit recovery-path validation before destructive actions are allowed.
Implement separate commands for value rotation, share changes, and project-key rotation, plus explicit recovery-path validation before destructive actions are allowed. Make the operator flow distinguish lost-key recovery/share reissue from compromised-key rotation.

**Step 4: Run test to verify it passes**

Expand Down
Loading