Skip to content
Merged
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
121 changes: 57 additions & 64 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ Muse Code subscription sign-in for opencode
[![npm](https://img.shields.io/npm/v/opencode-muse-auth)](https://www.npmjs.com/package/opencode-muse-auth)

opencode plugin that registers the **Muse Code subscription** login method for the built-in `meta`
provider. No API key to copy and no third-party CLI to install. On the V2 plugin API the plugin also
routes subscription inference through a local wire adapter that keeps requests compatible with Muse
Code's tool declarations; see [ADAPTER_CONTRACT.md](ADAPTER_CONTRACT.md). A successful login still
does not guarantee model access: Meta may reject or restrict this use.
provider: no API key to copy, no third-party CLI to install. On the V2 plugin API it also routes
subscription inference through a local wire adapter that keeps requests compatible with Muse Code's
tool declarations; see [ADAPTER_CONTRACT.md](ADAPTER_CONTRACT.md). A successful login still does not
guarantee model access: Meta may reject or restrict this use.

> **Unofficial and experimental.** This plugin uses a Muse Code subscription credential from
> opencode. Meta's terms limit that credential to Muse Code, so this use may not be supported and
Expand All @@ -29,41 +29,36 @@ Add the plugin to `opencode.json`; opencode installs it from npm:
}
```

## Sign in
## Quick start

1. Run `/connect` in opencode.
2. Pick the `meta` provider and the **Muse Code subscription** method.
3. Open the printed Meta device page, enter the printed code, and approve the login in your own
browser. The login completes by itself.
4. Select a `muse-*` model exposed by the `meta` provider, for example `meta/muse-spark-1.3`.
On opencode 2.x the plugin routes inference through the local adapter, so client tool names that
collide with Muse Code's builtins no longer trip Meta's client gate. The V1 plugin API still
sends requests straight to `api.meta.ai` and keeps receiving the 403.
3. Open the printed Meta device page, enter the code, and approve the login in your
browser; it completes by itself.
4. Select a `muse-*` model from the `meta` provider, for example `meta/muse-spark-1.3`.
Inference goes through the local adapter on the V2 plugin API.

The plugin mints a stable, account-bound inference key and hands it to opencode. The OAuth access
token from the login flow is kept in memory only and is never written to disk.
The plugin mints a stable, account-bound inference key for opencode; the OAuth access token
stays in memory and is never written to disk.

## Features

- Registers an `auth` hook for the built-in `meta` provider; there is no separate provider id to
configure.
- Runs the Meta device-code exchange (RFC 8628) inside `/connect`, then mints the subscription
inference key through the Model API.
- Registers an `auth` hook for the built-in `meta` provider (no separate provider id to
configure) and runs the Meta device-code exchange (RFC 8628) inside `/connect`, then mints
the subscription inference key through the Model API.
- Routes through the Responses API; the `meta.txt` system prompt applies automatically to `muse-*`
model ids.
- Runs a loopback wire adapter for the V2 plugin API: it aliases the client tool names that collide
with Muse Code's builtins on the way to `api.meta.ai`, restores them in the response, and
normalizes parameters Meta rejects (`tool_choice`, reasoning effort, output-token floor,
encrypted-reasoning include). The adapter's `/v1` origin becomes the per-request base URL; on
routes that ignore that override the plugin rewrites the assembled request onto the same origin.
Routing does not depend on the credential type: an existing `meta` API key and the V2 Muse OAuth
connection both go through the adapter; the OAuth access token only overrides `Authorization`
when the V2 sign-in is active. If Meta still refuses the request, the adapter retries once with
every tool aliased. See [ADAPTER_CONTRACT.md](ADAPTER_CONTRACT.md).
- V1 storage: the OpenCode auth store is the primary source, and the loader falls back to the
legacy cache only when the store has no key.
- V2 storage: the credential lives in the OpenCode integration store, and no local cache is
written on this path.
- Runs a loopback wire adapter for the V2 plugin API that aliases client tool names colliding
with Muse Code's builtins, restores them in responses, and normalizes parameters Meta rejects
(`tool_choice`, reasoning effort, output-token floor, encrypted-reasoning include). The
adapter's `/v1` origin becomes the per-request base URL, with the assembled request rewritten
onto it where the override is ignored. All `meta` connections use the adapter regardless of
credential type; the OAuth token overrides `Authorization` only while the V2 sign-in is
active. On refusal the adapter retries once with every tool aliased.
See [ADAPTER_CONTRACT.md](ADAPTER_CONTRACT.md).
- Stores the credential in the OpenCode auth store (V1) or integration store (V2); the V1
loader falls back to the legacy cache only when the store has no key, and V2 writes no
local cache.
- Never re-mints on its own; re-run `/connect` only after a 401, because the mint endpoint is
aggressively rate-limited.
- Flow parameters are compatible with the published behavior of oh-my-pi (MIT); see [NOTICE](NOTICE)
Expand All @@ -72,45 +67,45 @@ token from the login flow is kept in memory only and is never written to disk.
## Requirements

- opencode 2.2.4 or newer for inference through the adapter (the V2 plugin module shape; 2.2.0
loads the plugin but does not invoke its setup). On the V1 plugin API the plugin keeps
authenticating but still receives Meta's `client_attestation_denied` 403.
- Node.js 22.6 or newer for development; CI runs Node.js 22 and 24.
loads the plugin without invoking its setup). On the V1 plugin API the plugin still
authenticates but keeps receiving Meta's `client_attestation_denied` 403.
- Node.js 22.6 or newer for development (CI runs 22 and 24).
- An active Muse Code subscription on the Meta account used at login.

## Configuration

| Setting | Purpose | Default |
| --- | --- | --- |
| `plugins` entry in `opencode.json` | Loads the plugin. | not set |
| Auth store | Primary credential storage used by the V1 loader. | `~/.local/share/opencode/auth.json` |
| Legacy cache | Read-only fallback for credentials written by earlier versions. | `~/.config/opencode/muse-code-sub.json` |
| `/connect` method | Selects Muse Code subscription login for the `meta` provider. | `meta` provider |
| `MUSE_CODE_ADAPTER` | `off` keeps the local proxy but disables the tool-name reshaping and parameter normalization. | unset |
| Auth store | Credential storage read by the V1 loader. | `~/.local/share/opencode/auth.json` |
| Legacy cache | Read-only fallback from earlier versions. | `~/.config/opencode/muse-code-sub.json` |
| `/connect` method | Selects the Muse Code subscription login. | `meta` provider |
| `MUSE_CODE_ADAPTER` | `off` keeps the local proxy but disables reshaping and normalization. | unset |

## Privacy and security

The inference key is held by opencode: in the V1 auth store or the V2 integration store. The
opencode holds the inference key in the V1 auth store or the V2 integration store. The
legacy cache, when present, stores only `apiKey`, `accountId`, and `email`; the OAuth access token
is never written to disk. The plugin talks only to `auth.meta.com` for the device login and
is never written to disk. The plugin contacts only `auth.meta.com` for the device login and
`api.meta.ai` for key minting and inference. The V2 wire adapter listens on `127.0.0.1` only,
forwards the `Authorization` header unchanged, and never reads, stores or logs credentials,
request bodies or response bodies. See [SECURITY.md](SECURITY.md) for vulnerability reporting and
forwards `Authorization` unchanged, and never reads, stores or logs credentials or bodies.
See [SECURITY.md](SECURITY.md) for vulnerability reporting and
[ADAPTER_CONTRACT.md](ADAPTER_CONTRACT.md) for the inference wire rules.

To remove access:

- disconnect the provider or log out in opencode (the store is
`~/.local/share/opencode/auth.json`);
- delete `~/.config/opencode/muse-code-sub.json` if an older version created it;
- run `muse logout` if you also want to clear the Muse Code CLI session;
- run `muse logout` to also clear the Muse Code CLI session;
- remove `opencode-muse-auth` from `opencode.json`.

## Troubleshooting

### `/connect` shows no Muse Code subscription method

The plugin is not loaded. Check the JSON syntax of `opencode.json`, the plugin name spelling, and
the package installation, then restart opencode.
The plugin is not loaded. Check the `opencode.json` syntax, the plugin name spelling, and the
package installation, then restart opencode.

### Requests fail with 401 or access was revoked

Expand All @@ -119,38 +114,36 @@ its own.

### Meta rejects subscription requests from this client

Meta may return a 403 mentioning `client_attestation_denied` or that Muse Code subscriptions cover
the Muse Code client only. With the V2 adapter running this should not happen for tool names that
collide with Muse Code's builtins. If it appears anyway, one of these is true:
Meta may return a 403 mentioning `client_attestation_denied`, or that Muse Code subscriptions cover
the Muse Code client only. With the V2 adapter running this should not happen for colliding tool
names. If it appears anyway, one of these is true:

- the adapter could not bind its loopback port; opencode's log records
`the local request adapter could not start`, and the provider stays on the direct endpoint;
`the local request adapter could not start` and the provider stays on the direct endpoint;
- `MUSE_CODE_ADAPTER=off` is set, which keeps the proxy but disables the reshaping;
- you are on the V1 plugin API, which has no adapter;
- the plugin is older than 0.5.1 or opencode is older than 2.2.4: 0.5.0 skipped the adapter when
the active `meta` credential was a plain API key, and opencode 2.2.0 loads the plugin without
invoking its V2 setup. Check `opencode plugin list` and `opencode --version`, then update;
- you are on the V1 plugin API, which has no adapter, or on a plugin or opencode release
that predates the adapter wiring: first-generation adapter releases skipped plain `meta`
API-key credentials, and early opencode V2 hosts load the plugin without invoking its V2
setup. Check `opencode plugin list` and `opencode --version`, then update;
- Meta changed the gate, for example to a real per-client signature. Nothing in this plugin can
forge one.

This is a client restriction, not evidence of an expired key. Signing in again does not remove it.
This is a client restriction, not an expired key, so signing in again does not remove it.
Use Muse Code for the subscription, or a separate pay-as-you-go Meta Model API key for other
clients, subject to Meta's terms.

### The login times out or is denied

Approve the printed code in the browser before the flow expires, then retry. A login rejected by
Meta usually means the account has no active Muse Code subscription.
Approve the printed code before the flow expires, then retry. A Meta rejection usually means the
account has no active Muse Code subscription.

### A legacy cache file is still present

It is harmless: the V1 loader prefers the OpenCode auth store and reads the cache only when the
store has no key. Delete the file if you are migrating to V2 storage.
Harmless: the V1 loader prefers the OpenCode auth store and reads the cache only when the store
has no key. Delete the file when migrating to V2 storage.

## Development

Requirements: Node.js 22.6 or newer and npm.

```bash
git clone https://github.com/TheStreamCode/opencode-muse-auth.git
cd opencode-muse-auth
Expand All @@ -159,15 +152,15 @@ npm run build
npm test
```

`npm test` builds with `tsc` and runs the TypeScript test suite with the Node test runner. CI runs
`npm test` builds with `tsc` and runs the TypeScript suite with the Node test runner. CI runs
the same gate on Node.js 22 and 24; Node 22 needs 22.6 or newer for test-runner type stripping.
No Meta account is needed for the test suite.
The suite needs no Meta account.

## Support

Usage questions and workflows belong in GitHub Discussions; reproducible defects belong in GitHub
Issues using the bug report form. See [SUPPORT.md](SUPPORT.md). Problems in opencode or Meta
themselves belong to their respective projects.
Usage questions belong in GitHub Discussions; reproducible defects in GitHub Issues via the
bug report form. See [SUPPORT.md](SUPPORT.md). Problems in opencode or Meta belong to their
respective projects.

## Contributing

Expand Down
Loading