# Split-GPG Setup with Gaol Vaults

This documents how to set up GPG key isolation using gaol vaults.

## Overview

The vault container holds private GPG keys with no network access. Client containers can perform cryptographic operations via agent socket forwarding.

## Current Implementation (Key Import Method)

Due to GPG 2.4+ keyboxd architecture limitations, the current implementation imports the secret key into client containers. This provides convenience but not true key isolation.

### Setup Steps

1. **Create the vault:**
   ```bash
   sudo gaol vault create research-keys --type gpg
   sudo gaol vault start research-keys
   ```

2. **Generate GPG key in vault:**
   ```bash
   sudo machinectl shell root@vault-research-keys /bin/bash -c "
     gpg --batch --pinentry-mode loopback --gen-key <<EOF
   Key-Type: eddsa
   Key-Curve: ed25519
   Key-Usage: sign
   Subkey-Type: ecdh
   Subkey-Curve: cv25519
   Subkey-Usage: encrypt
   Name-Real: Research
   Name-Email: research@localhost
   Expire-Date: 2y
   %no-protection
   %commit
   EOF"
   ```

3. **Export and import key to client container:**
   ```bash
   # Export from vault
   sudo machinectl shell root@vault-research-keys /bin/bash -c \
     'gpg --armor --export-secret-keys research@localhost' \
     > /tmp/secret-key.asc

   # Import to client
   sudo machinectl copy-to research /tmp/secret-key.asc /tmp/secret-key.asc
   sudo machinectl shell root@research /bin/bash -c \
     'gpg --batch --import /tmp/secret-key.asc'
   ```

## True Split-GPG (Socket Forwarding)

For proper key isolation where private keys never leave the vault, socket forwarding is required. This is more complex with GPG 2.4+.

### Architecture

```
┌─────────────────────┐     ┌─────────────────────┐
│   Vault Container   │     │  Client Container   │
│   (no network)      │     │                     │
│                     │     │                     │
│ ┌─────────────────┐ │     │ ┌─────────────────┐ │
│ │ Private Keys    │ │     │ │ Public Keys     │ │
│ │ ~/.gnupg/       │ │     │ │ Key Stubs       │ │
│ │ private-keys-   │ │     │ │ (no private)    │ │
│ │ v1.d/*.key      │ │     │ └─────────────────┘ │
│ └─────────────────┘ │     │          │          │
│          │          │     │          ▼          │
│          ▼          │     │ ┌─────────────────┐ │
│ ┌─────────────────┐ │     │ │ GPG commands    │ │
│ │ gpg-agent       │◄──────┼─│ use forwarded   │ │
│ │ extra-socket    │ │     │ │ agent socket    │ │
│ └─────────────────┘ │     │ └─────────────────┘ │
└─────────────────────┘     └─────────────────────┘
        ▲                            │
        │      Socket Forwarding     │
        └────────────────────────────┘
```

### Requirements

- GPG 2.1.1+ on both containers
- Shared socket directory via bind mount
- Client has public keys but NO private key files
- Agent extra-socket used for forwarding (not main socket)

### GPG 2.4+ keyboxd Challenges

Modern GPG uses `keyboxd` which complicates split-GPG:

1. **No pubring.kbx file** - Keys stored in SQLite via daemon
2. **Keyboxd may auto-start** - Can replace forwarded sockets
3. **Socket lifecycle** - systemd may delete socket directories

**Workarounds:**

```bash
# Disable keyboxd (in ~/.gnupg/common.conf)
# use-keyboxd  # Comment out this line

# Or use --no-autostart for remote operations
gpg --no-autostart --list-keys
```

### Vault Container Setup

```bash
# gpg-agent.conf
extra-socket /var/shared/gnupg/S.gpg-agent.extra
default-cache-ttl 1800
max-cache-ttl 7200
allow-loopback-pinentry
```

```bash
# Create shared socket directory
mkdir -p /var/shared/gnupg
chmod 700 /var/shared/gnupg
ln -sf /var/shared/gnupg /run/user/0/gnupg
gpgconf --launch gpg-agent
```

### Client Container Setup

```bash
# Bind mount in nspawn config
[Files]
BindReadOnly=/run/gaol/vault-name/gnupg:/run/vault-gpg

# Disable local gpg-agent socket activation
systemctl --user --global mask gpg-agent.socket gpg-agent-*.socket

# Symlink to vault socket
ln -sf /run/vault-gpg/S.gpg-agent.extra /run/user/0/gnupg/S.gpg-agent
```

### Key Stubs

For true split-GPG, client containers need "key stubs" - markers that tell GPG the key exists but is held elsewhere:

```bash
# In client container, keys show with '>' indicator
gpg --list-secret-keys
# sec>  ed25519 2026-02-08 [SC]
#       (the '>' means key is on smartcard/remote agent)
```

Creating stubs without private keys requires either:
- Hardware token/smartcard workflow
- Manual stub file creation (undocumented)
- QubesOS-style split-gpg tooling

## QubesOS Split-GPG Reference

QubesOS implements the gold standard for split-GPG:

- Uses `qrexec` RPC framework for socket forwarding
- Each operation requires user approval (visual prompt)
- Only secret subkeys copied to backend (not primary key)
- Separate GNUPGHOME per client connection

See: https://www.qubes-os.org/doc/split-gpg/

## Future Improvements

1. Implement RPC-style consent prompts for operations
2. Create stub-only key export tooling
3. Handle keyboxd properly for GPG 2.4+
4. Systemd service for socket lifecycle management
