npm package: @ramarivera/opencode-fnox-sync
Description: OpenCode plugin that syncs auth.json through fnox
Latest version: 0.1.4

# opencode-fnox-sync

OpenCode plugin that keeps `auth.json` synchronized through `fnox`.

`fnox` is a hard dependency by design. The plugin does not decide how secrets are stored; users configure that in `fnox` itself with `age`, `1password`, `vault`, cloud secret stores, or any other supported provider strategy.

## Setup wizard

Run the setup wizard to generate `opencode-fnox-sync.json`:

```bash
bunx opencode-fnox-sync-setup
```

The wizard is built with `@clack/prompts`, so it installs with the package and does not require an external TUI binary.

For non-interactive setup, you can generate a config file directly:

```bash
bunx opencode-fnox-sync-setup --yes --project-local
```

For locally encrypted workflows such as `fnox + age` or `fnox + sops`, the wizard can also configure an optional managed git transport so the plugin handles the pull/commit/push loop around `fnox` automatically.

When managed git sync is enabled, the wizard can also write a starter `fnox.toml.example` into the managed repo path so you have an `age`-based template to start from.

## What it does

- pulls a remote auth blob from `fnox` when OpenCode starts
- optionally restores that blob into the local OpenCode auth file
- watches the local auth file for token refreshes
- pushes the updated auth blob back into `fnox`
- avoids re-pushing the same content by hashing the auth file

## Install

Install `fnox` first. The upstream project recommends installing it with `mise`:

```bash
mise use -g fnox@1.17.0
```

Add the package to your OpenCode config:

```json
{
  "plugin": [
    "@ramarivera/opencode-fnox-sync"
  ]
}
```

## Plugin config

The plugin reads config from one of these files:

1. `<project>/opencode-fnox-sync.json`
2. `~/.config/opencode/opencode-fnox-sync.json`

On all platforms, the default OpenCode auth path matches OpenCode's own XDG-style storage layout:

- Linux: `~/.local/share/opencode/auth.json`
- macOS: `~/.local/share/opencode/auth.json`
- Windows: `%USERPROFILE%\.local\share\opencode\auth.json`

Example:

```json
{
  "$schema": "https://raw.githubusercontent.com/ramarivera/opencode-fnox-sync/main/schema.json",
  "enabled": true,
  "secretName": "OPENCODE_AUTH",
  "workingDirectory": "/path/to/project-with-fnox-toml",
  "profile": "dev",
  "startupSync": "prefer-remote",
  "watchAuthFile": true,
  "debounceMs": 1000,
  "managedSync": {
    "type": "git",
    "repositoryPath": "/path/to/private-fnox-secrets-repo",
    "pullOnStartup": true,
    "pullBeforeSet": true,
    "pushOnSet": true
  }
}
```

## fnox model

The plugin always shells out to `fnox`. Users choose the storage model in `fnox`, not in the plugin.

When `managedSync.type = "git"`, `fnox` still remains the core secret abstraction. The plugin only automates transport for locally encrypted backends by:

- pulling the git-backed `fnox` repo on startup
- optionally pulling again before `fnox set`
- committing only tracked `fnox*.toml` files after updates
- optionally pushing the encrypted changes upstream

Example `fnox.toml` using `age` for low-friction cross-device sync:

```toml
[providers.age]
type = "age"
recipients = [
  "age1exampledeviceone...",
  "age1exampledevicetwo..."
]

[secrets]
OPENCODE_AUTH = { provider = "age" }
```

If you want to bootstrap from `1password` but avoid repeated prompts later, use `fnox` sync or caching patterns so the hot path is not the password manager itself.

## Choosing a fnox backend

Given your earlier goal of cross-device sync without constant biometric prompts, the most convenient backend is usually:

1. `age` as the steady-state storage backend
2. one recipient per device
3. optional `1password` only for bootstrap or recovery

Why `age` is the best fit here:

- no repeated Touch ID or Windows Hello prompt during normal OpenCode use
- easy to share one encrypted secret across multiple devices
- works well with git-backed `fnox.toml` workflows
- keeps the plugin simple because `fnox get` stays the hot path

Good alternatives only if you already run them everywhere:

- Vault / cloud secret managers: best for existing infra-heavy setups
- 1Password direct provider: okay for occasional/manual use, less ideal for frequent auth refresh sync

## Logging

The plugin writes runtime logs to a file under the XDG state directory rather than using console output:

- Linux/macOS default: `~/.local/state/opencode-fnox-sync/opencode-fnox-sync.log`
- Windows default: `%USERPROFILE%\.local\state\opencode-fnox-sync\opencode-fnox-sync.log`

This keeps plugin logs near the canonical state/config area without polluting the OpenCode TUI.

## Publishing prep

The package now includes npm metadata and a `prepublishOnly` gate that runs:

- `bun run typecheck`
- `bun test`
- `bun run build`

Release automation is defined in:

- `.github/workflows/ci.yml`
- `.github/workflows/release.yml`
- `PUBLISHING.md`

The intended release model is:

- first publish manually with `npm login` + `npm publish`
- later publishes via GitHub Actions trusted publishing from `release.yml`

I also checked npm availability during development and `@ramarivera/opencode-fnox-sync` was not present in the registry at the time of checking.

## Behavior

`startupSync` controls first-run conflict handling:

- `prefer-remote`: restore remote into the local file when the hashes differ
- `prefer-local`: push the local file into `fnox` when the hashes differ
- `remote-if-missing`: only restore if the local file is absent

## Development

```bash
bun install
bun test
bun run typecheck
bun run build
```