# `rho crypto`

## Purpose

`rho crypto` owns signing, verification, encryption, decryption, and repo-level crypto policy.

It exists so that Rho can keep:

- identity separate from key usage
- crypto separate from repo policy
- crypto separate from transport adapters

## Responsibilities

- sign files and verify signatures
- encrypt files for one or more recipients
- decrypt files for authorized recipients
- manage repo-level crypto policy metadata
- expose deterministic crypto operations to other `rho` commands

## Git Encryption UX

Transcrypt is useful prior art for Git-native encrypted files. It configures Git
clean/smudge filters, `diff` text conversion, merge drivers, `.gitattributes`
patterns, and a pre-commit check so selected paths are encrypted in commits but
decrypted in the local working tree.

Rho should borrow that UX shape, but not copy Transcrypt's crypto model as the
core design.

Transcrypt's implementation is intentionally repo-password based:

- encryption configuration is stored in local Git config
- file selection is driven by `.gitattributes` filter patterns
- clean/smudge filters call OpenSSL symmetric encryption/decryption
- clone setup requires sharing or importing the same cipher/password
- contexts are additional symmetric passwords for different path groups

Rho needs a different default:

- identity-linked public keys, not shared repo passwords
- recipient-based encryption for one or more participants
- signed envelopes for messages, requests, approvals, and releases
- explicit host-side decrypt/stage operations before agent reads
- no private keys mounted into agent sandboxes
- authenticated encryption by default

## Rho Envelope First

Implement explicit encrypted envelopes before transparent Git filters.

Initial commands:

```bash
rho crypto sign <path> --identity <profile>
rho crypto verify <path> --signature <path>
rho crypto encrypt <path> --key-file <path> --out <path.rhoenc>
rho crypto decrypt <path.rhoenc> --key-file <path> --out <path>
rho crypto seal <path> --recipient <identity-id> [--recipient <identity-id>...] --out <path.rhoenc> --repo-root <repo> --path <repo-path> [--purpose <purpose>]
rho crypto open <path.rhoenc> --identity <identity-id> --out <path> --repo-root <repo> --path <repo-path> [--purpose <purpose>]
```

The first implementation uses a repo-local symmetric key file for the
transparent Git layer. That key lives outside the repository, normally under
`~/.rho/repo-keys/`, and the committed artifact is a `rho_transparent_file`
YAML envelope containing OpenSSL ciphertext. This deliberately mirrors
Transcrypt's useful Git behavior while keeping the implementation behind Rho
commands instead of depending on Transcrypt itself.

Participant-oriented envelopes are still the target for private messages and
results. The transparent repo filter is a convenience for protected paths, not
the final authorization model.

## Recipient Envelopes

`rho crypto seal` encrypts one plaintext file for one or more readers:

```bash
rho crypto seal result.yaml \
  --recipient rho://id/github/user1 \
  --recipient rho://id/github/user2 \
  --out rho/messages/inbox/id/github/user1/msg-123/result.yaml.rhoenc \
  --repo-root . \
  --path rho/messages/inbox/id/github/user1/msg-123/result.yaml.rhoenc \
  --purpose rho.bundle
```

Each recipient must have an imported identity bundle containing a public
`x25519-encryption` key. Those public bundles are intended to be committed under
`rho/participants/` after the repo admin accepts and verifies the participant.
The sender imports those bundles locally, seals the file, and commits only the
encrypted envelope. Recipient envelopes are always bound to a repo id,
repo-relative path, and purpose in the recipient key derivation. Rho also
expands the explicit recipients with current repo owners/admins and the active
sender identity. Opening with the same repo flags rejects envelopes copied to
the wrong path.

Each reader opens the same committed envelope with their local private identity:

```bash
rho crypto open result.yaml.rhoenc \
  --identity rho://id/github/user1 \
  --out result.yaml \
  --repo-root . \
  --path rho/messages/inbox/id/github/user1/msg-123/result.yaml.rhoenc \
  --purpose rho.bundle
```

V1 envelopes use X25519 ECDH via OpenSSL. For each recipient, Rho creates an
ephemeral X25519 key, derives a shared secret with the recipient public key, and
stores a recipient-specific ciphertext entry. This is intentionally simple and
inefficient for large files, but it proves the important collaboration behavior:
one committed artifact can be read by multiple approved identities without
sharing a repo password.

## SSH Signatures First

V1 signing should reuse the SSH signing key selected or generated by `rho id`.

Initial command shape:

```bash
rho crypto sign request.yaml \
  --identity rho://id/github/user2 \
  --out request.yaml.rhosig.yaml

rho crypto verify request.yaml \
  --signature request.yaml.rhosig.yaml \
  --identity rho://id/github/user2
```

The signature sidecar should be detached YAML:

```yaml
version: 1
signature:
  signed_path: request.yaml
  signed_sha256: <sha256>
  signer: rho://id/github/user2
  key_id: rho://key/github/user2/ssh-ed25519-1
  algorithm: ssh-ed25519
  namespace: rho
  signature: |
    -----BEGIN SSH SIGNATURE-----
    ...
    -----END SSH SIGNATURE-----
  created_at: 2026-05-01T00:00:00Z
```

Signing requires a local identity in `RHO_HOME`. Verification may use either a
local identity or an imported peer identity bundle.

Suggested envelope shape:

```yaml
version: 1
kind: rho_encrypted_file
crypto:
  algorithm: xchacha20poly1305
  recipients:
    - identity_id: rho://id/github/user1
      key_id: rho://key/github/user1/age-x25519-1
      wrapped_key: <base64>
  sender:
    identity_id: rho://id/github/user2
    key_id: rho://key/github/user2/ed25519-1
  signature:
    algorithm: ed25519
    value: <base64>
payload:
  content_type: application/vnd.rho.message+yaml
  nonce: <base64>
  ciphertext: <base64>
```

Exact algorithms can change during implementation, but the important property is
that a committed artifact is self-describing, recipient-bound, and
signature-verifiable without relying on clone-local Git filter state.

## Transparent Filters Later

`rho repo protect-path` installs a Transcrypt-like convenience layer:

```bash
rho repo protect-path "secrets/**"
```

That layer uses `.gitattributes` and local Git clean/smudge filters. The filters
call:

```bash
rho crypto clean --key-file <key>
rho crypto smudge --key-file <key>
```

The clean filter turns worktree plaintext into a `rho_transparent_file` envelope
for the Git index. The smudge filter turns committed envelopes back into
worktree plaintext for users who have the repo key. `rho repo protect-path` also
installs a pre-commit hook that rejects protected staged blobs that do not look
like Rho encrypted envelopes, which catches commits made without the configured
filter path.

When a protected path has recipient policy, the same clean/smudge entry uses:

```bash
rho crypto clean --repo-root <repo> --path <path>
rho crypto smudge --repo-root <repo> --path <path>
```

`clean` should read the repo permissions policy, match the file path, and write
a `rho_recipient_envelope` for the configured readers. `smudge` tries the local
identities in `RHO_HOME`; if one can open the envelope, the working tree receives
plaintext. If none can open it, the ciphertext is left intact.

The implementation reads encrypted path policy from `rho/policy/permissions.yaml`.
`rho/policy/crypto.yaml` only declares crypto mechanisms and compatibility
settings. The split is:

```yaml
# rho/policy/permissions.yaml
version: 1
permissions:
  - id: direct_inbox
    pattern: rho/messages/inbox/{member.handle}/**
    expand: members
    write:
      allowed:
        - members
    read:
      encrypted_to:
        - "{member.id}"
```

```yaml
# rho/policy/crypto.yaml
version: 1
crypto:
  signatures: ssh
  encryption: x25519-recipient-envelope
```

Transparent filters are a convenience layer only. `rho messages`, `rho run`, and
`rho middleware` must continue to enforce explicit policy and should not rely on
Git filters as the security boundary.

## Preserve From The Old System

- explicit crypto as a distinct architectural seam
- recipient-oriented encryption
- signed artifact handling

## Replace Old Functionality With

- a small Rust crypto crate used by the CLI commands
- simple file signing and multi-recipient encryption commands
- explicit policy metadata checked by `rho repo`, `rho messages`, and `rho run`

## Leave Off In V1

- key rotation
- recovery mnemonics unless clearly needed
- custom envelope migration complexity
- streaming encryption for very large files

## Important Rule

`rho crypto` should provide cryptographic operations.
It should not decide who is allowed to act.

Authorization stays in repo policy and middleware.

Agents should not receive private key material. If an agent needs to read an
encrypted message, host-side Rho code should verify/decrypt that message into a
temporary staged path and mount only that staged plaintext into the sandbox.
