# Sopsy — Member Guide

You just cloned a repository whose secrets are managed by `sopsy`. This guide gets
you from "I can't read `.env.encrypted`" to "I edit secrets every day without
thinking about it."

> [!NOTE]
> sopsy does not replace SOPS — it wraps it. Everything here ultimately runs
> `sops` + `age`; sopsy just makes the workflow pleasant and hard to get wrong.
> For the full command/flag reference, see the [README](../README.md).

## Table of Contents

- [Mental model in 30 seconds](#mental-model-in-30-seconds)
- [One-time setup](#one-time-setup)
- [Getting access with `sopsy join`](#getting-access-with-sopsy-join)
- [Day-to-day workflow](#day-to-day-workflow)
- [Before you commit](#before-you-commit)
- [Troubleshooting with `sopsy doctor`](#troubleshooting-with-sopsy-doctor)
- [FAQ](#faq)

---

## Mental model in 30 seconds

- Secrets live in Git **encrypted** (`.env.encrypted`, `*.encrypted`).
- You can decrypt them only once your **public key** is listed as a recipient in
  `.sops.yaml`, because the matching **private key** lives in *your* Secure Enclave.
- Your private key never leaves your Mac. You only ever share your public key.
- Plaintext `.env` is gitignored and must never be committed.

```mermaid
flowchart LR
    you["You<br/>(Secure Enclave private key)"] -- "sopsy edit" --> sops[sops + age]
    sops -- decrypt --> plain["plaintext in your editor"]
    plain -- save --> sops
    sops -- re-encrypt --> enc[".env.encrypted in Git"]
    repo[".sops.yaml lists your public key"] -. authorizes .-> sops
```

## One-time setup

Install the toolchain and sopsy (macOS):

```bash
# Simplest:
brew install kigster/tap/sopsy

# Requires Rust toolchain
cargo install sopsy
sopsy deps # installs additional binaries
```

Confirm your machine is ready:

```bash
sopsy doctor
```

> [!TIP]
> You want green checks for `sops`, `age-plugin-se`, and `git` under **Tools**, and
> (on Apple Silicon) "Secure Enclave available" under **System**. If something is
> red, fix that first — every other command depends on it.

## Getting access with `sopsy join`

Onboarding is **self-service**: you generate your own key and open a pull request;
any existing member approves you. You never have to email anyone your key or wait on
one specific busy person.

```mermaid
sequenceDiagram
    actor You
    participant SE as Your Secure Enclave
    participant PR as Pull request
    actor Member as Any active member
    participant Repo as .sops.yaml / .sopsy.yml

    You->>SE: sopsy join alice  (generates key, Touch ID)
    SE-->>You: public key recorded as "pending" in .sopsy.yml
    You->>PR: commit + open a pull request
    Member->>PR: check out your branch
    Member->>Repo: sopsy approve alice
    Note over Member,Repo: vouches, adds your key, re-keys every secret
    Member->>PR: commit + merge
    You->>Repo: git pull && sopsy edit .env.encrypted ✅
```

### Step 1 — request access

From inside the cloned repo:

```bash
sopsy join alice          # use your own handle instead of "alice"
```

This generates your Secure Enclave identity (Touch ID may prompt; the private key
never leaves the chip), shows your public key, and records a **pending** entry in
`.sopsy.yml` with a timestamp. A pending entry is *not* in `.sops.yaml`, so it
grants nothing yet — it is purely a request.

> [!TIP]
> Already have a Secure Enclave public key? Skip generation with
> `sopsy join alice --public-key age1se1…`.

### Step 2 — open a pull request

```bash
git add .sopsy.yml
git commit -m "join: request access for alice"
git push           # then open a PR, and ping any member to approve you
```

### Step 3 — get approved

Any active member checks out your branch and runs `sopsy approve alice`. That adds
your key to `.sops.yaml`, re-keys every encrypted file so your key can open it, and
flips you to `active`. They commit and merge.

> [!IMPORTANT]
> Approve promptly: your request expires after the repo's `join_request_ttl`
> (default 72h). If it goes stale, just re-run `sopsy join` to refresh the
> timestamp.

Once it's merged, `git pull` and you're in.

> [!NOTE]
> No Apple Silicon / Secure Enclave? You can still participate with a software `age`
> key (`age-keygen -o ~/.config/sops/age/keys.txt`) and pass that public key to
> `sopsy join alice --public-key age1…` — you just don't get hardware protection.
> Point `SOPS_AGE_KEY_FILE` at your key file so `sops` can find it.

## Day-to-day workflow

```bash
git pull                        # get the latest encrypted secrets
sopsy edit .env.encrypted       # decrypt → edit → re-encrypt on save
sopsy check                     # confirm hygiene before committing
git add .env.encrypted
git commit -m "Update API keys"
git push
```

- **`sopsy edit <file>`** decrypts into a temp file, opens your `$EDITOR` (or
  `--editor`), and re-encrypts when you save and quit. A Touch ID prompt may appear
  — that's the Secure Enclave releasing your key for this operation.
- To read a value without editing, use `sops` directly:
  `sops --decrypt --input-type dotenv .env.encrypted`.
- New encrypted file? Name it so it matches a `.sops.yaml` rule (e.g.
  `something.encrypted` or `config/db.encrypted.yaml`) and run
  `sops --encrypt --in-place …`, or ask a teammate.

> [!WARNING]
> Never copy a decrypted value into a tracked file, and never `git add .env`.
> The plaintext `.env` is for your machine only. If you need a real local `.env`,
> generate it from the encrypted source:
> `sops --decrypt --input-type dotenv .env.encrypted > .env`.

## Before you commit

Always run the same gate CI will run:

```bash
sopsy check
```

It verifies, among other things, that `.env` isn't tracked, that every encrypted
file is genuinely encrypted, and that a break-glass key exists. A non-zero exit
means **do not commit** until it's green. See the
[CI gate diagram in the README](../README.md#sopsy-check) for the full list of the
seven invariants.

> [!TIP]
> Wire it into a pre-commit hook so you never forget:
> ```bash
> echo 'exec sopsy check' > .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
> ```

## Troubleshooting with `sopsy doctor`

`sopsy doctor` is your first stop for anything weird. It never fails and is safe to
paste into a Slack thread or GitHub issue.

```mermaid
flowchart TD
    problem([Something's wrong]) --> doc[run: sopsy doctor]
    doc --> tools{Tools all green?}
    tools -- no --> brew["brew install sops age age-plugin-se"]
    tools -- yes --> repo{Inside a git repo<br/>with .sops.yaml?}
    repo -- no --> clone["cd into the repo / git init"]
    repo -- yes --> dec{sopsy edit fails to decrypt?}
    dec -- yes --> access["Your key isn't a recipient yet →<br/>run sopsy join and get approved"]
    dec -- no --> done([likely environment-specific —<br/>share doctor output])
```

| Symptom                                            | Likely cause & fix                                                             |
| -------------------------------------------------- | ----------------------------------------------------------------------------- |
| `sops` / `age-plugin-se` not found on PATH         | `brew install sops age age-plugin-se`, then re-open your shell.               |
| `edit` fails: *no matching creation rules*         | The file name doesn't match a `.sops.yaml` `path_regex`. Rename or ask.       |
| `edit` fails to decrypt (no key)                   | Your key isn't in `.sops.yaml` yet — run `sopsy join` and get approved.       |
| Touch ID never prompts / decrypt hangs             | Enclave/Touch ID not enrolled. Check `sopsy doctor` **System** group.         |
| `not inside a git repository`                      | Run commands from within the cloned repo (or `git init`).                     |
| `.sopsy.yml not found — run sopsy init`            | The repo wasn't bootstrapped with sopsy; the owner should run `sopsy init`.   |

## FAQ

**Do I ever run `sopsy init`?** No — that's the one-time owner action that
bootstraps the repo. You join an already-initialized repo with `sopsy join`.

**Can I approve other people?** Yes. Any *active* member can run `sopsy approve` —
the cryptography only requires that you can already decrypt. Vouch carefully: you're
confirming the key really belongs to that person.

**Can I use VS Code?** Yes: `sopsy edit .env.encrypted --editor "code --wait"`
(the `--wait` is essential so sops knows when you've finished editing).

**I rotated/lost my Mac.** Run `sopsy join` on the new machine to request a fresh
key, get approved, and have someone `sopsy recipient remove` the old one. Your old
device's key can no longer decrypt new commits once removed.

> [!CAUTION]
> If your Mac is the *only* recipient and it dies without a break-glass key in
> place, the secrets are gone for good. Make sure your team has a break-glass key —
> `sopsy check` fails until one exists precisely to prevent this.
