Skip to content
Chasewhip8Public

About

An opinionated OpenCode setup powered by Nix.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Limitless

A Home Manager module for a ready-to-use OpenCode 2 workspace.

Limitless pins OpenCode 2.2.5, supplies coding agents, artifact and source-research tools, and configures optional first-party MCP connections. OpenCode owns its service discovery, OAuth credentials, Code Mode, and native notification delivery. Home Manager owns packages and non-secret configuration, with optional Linux service supervision.

Use it

{
  inputs.limitless.url = "github:your-org/limitless";

  outputs = { home-manager, limitless, ... }: {
    homeConfigurations.me = home-manager.lib.homeManagerConfiguration {
      modules = [
        limitless.homeModules.default
        { programs.limitless.enable = true; }
      ];
    };
  };
}

Run opencode, or its oc shell alias, from a project. It discovers or starts its native background service. Connect model providers through /connect.

Limitless disables OpenCode's built-in browser plugin (opencode.browser) to avoid conflicts with T3 Code's browser tools. Use them for browser access.

Included capabilities

  • Agents: limitless, a Research subagent, and one Oracle for technical and design judgment. Only Limitless delegates.
  • Clients: an optional supervised T3 Code environment served by the managed OpenCode service, plus opt-in desktop apps for OpenCode and T3 Code.
  • Skills: repository-setup for parallel worktrees, commit-and-pr for convention-following commits and PR delivery through green CI, and behavior-model for evidence-backed behavioral models.
  • Artifacts: project-scoped folders under .limitless/artifacts/.
  • Source research: optional guarded GitHub checkouts under .limitless/repos/.
  • Service integrations: optional Atlassian, Notion, Sentry, Linear, and GitHub MCP presets with account-specific connection names and audited reads for Research.
  • Anthropic subscription authentication: a pinned Claude Pro/Max plugin.
  • Git hygiene: .limitless/ is globally ignored by default.

The plugin exposes artifact_create and artifact_list directly with codemode = false. Enabling github.enable adds github_clone; disabling it removes that tool even when repository access remains configured. MCP tools use OpenCode's native Code Mode. Every Limitless tool resolves the invoking session's location.directory as its project root.

MCP connections

For a shared self-hosted OAuth gateway, see Executor. Direct connections remain the default and can stay in place during a gateway pilot.

Each entry names one connection. Enable only the services you use:

programs.limitless.mcp.servers = {
  atlassian.preset = "atlassian";
  notion-work.preset = "notion";
  notion-personal.preset = "notion";
  sentry.preset = "sentry";
  linear.preset = "linear";
};

After applying Home Manager, run /mcps, select each connection, and sign in. Connections are ready only when OpenCode reports them as connected. Notion work and personal entries have separate OAuth identities even though their URLs match; authorize the intended workspace for each. Renaming a connection creates a new credential identity. Token refresh and OAuth state belong to OpenCode.

settings overlays the preset using native OpenCode V2 server fields. For example:

programs.limitless.mcp.servers.sentry = {
  preset = "sentry";
  settings.timeout.execution = 120000;
};

Set settings.disabled = true to retain a configured connection without connecting it. Additional servers can use programs.limitless.opencode.settings.mcp.servers. Use one configuration path per server name. Native-only servers receive the same role-based permissions, with no automatic read exceptions for Research.

GitHub authentication

The hosted GitHub server requires an explicitly configured registered OAuth client or a PAT. Generic OAuth discovery alone is insufficient. For a registered client:

programs.limitless.mcp.servers.gh = {
  preset = "github";
  settings.oauth.client_id = "{env:GITHUB_MCP_CLIENT_ID}";
};

Include the native oauth.client_secret and callback settings when required by your registered application. Alternatively, supply a PAT through the OpenCode process environment:

programs.limitless.mcp.servers.gh = {
  preset = "github";
  settings = {
    oauth = false;
    headers.Authorization = "Bearer {env:GITHUB_MCP_TOKEN}";
  };
};

Keep token values out of Nix expressions and generated files. Ensure the credential is available to the background service; exporting a variable in a new terminal does not change an already-running service's environment. The preset enables context, repository, issue, pull-request, and Actions toolsets. GitHub MCP authentication is separate from Git clone/fetch/push credentials and the guarded research clone configuration below. Limitless does not install gh.

Use gh or github-mcp as the connection name. The name github overlaps the local github_clone tool. The module rejects overlapping tool namespaces and account prefixes such as notion plus notion_work; use notion-work instead.

Permissions and coverage

Tool access follows the agent's role:

Agents Access
limitless Automatic approval for reads, writes, shell commands, and MCP tools except T3 delegation and the Research-only executor-read connection
oracle Read/write MCP access; external mutations require explicit delegation of user-authorized actions. Investigative shell and edits; no T3 tools or subagents; deny artifact creation and session changes
research Executor's read-only connection, or audited reads in direct-MCP mode; local tools for source discovery, with code changes and diagnostic experiments excluded by instructions; no T3 tools; deny external mutations, artifact creation and session changes

With Executor enabled, Research can use only executor-read; Executor's connection policy owns read-only enforcement. Direct MCPs are denied to Research, even while retained for Limitless and Oracle. Without Executor, direct-MCP mode uses deny rules plus exact audited read exceptions for Research. Read tools such as web fetch and artifact listing inherit the global allow rule.

Sensitive-file reads and potentially destructive shell commands no longer have default approval prompts. Shared instructions still require explicit user direction for destructive work. Managed-checkout edits remain denied.

In direct-MCP mode, readTools replaces a preset's exact read-tool list for Research. Add names only after checking their behavior and the authenticated catalog. Wildcards are rejected. The optional https://mcp.linear.app/mcp/readonly endpoint can further restrict a Linear connection at the server. Linear's read-tool list is empty until verified names from an authenticated catalog are configured.

These rules control named-tool access. Shell commands can write files or call services, and Code Mode's raw HTTP fetch can send write requests. Research instructions forbid external mutations; Oracle requires explicit delegation of user-authorized MCP writes. The primary agent owns implementation and integration; the tool permissions do not provide a sandbox. Later project configuration or agent definitions can override global permissions. Project-local MCP additions need their own permission rules. A configured server must be trusted to implement its advertised read operations correctly.

For T3's role-specific tool access and shared-service setup, see T3 Code.

Preset Daily workflows Coverage limits
Atlassian Jira and Confluence search, reads, edits, comments, transitions Organization policies and enabled tool groups determine access. Uses the v2 flat catalog.
Notion Pages, databases, comments, queries, uploads Workers deployment and generic API administration are outside this preset. Tools can depend on plan.
Sentry Issues, events, traces, debugging Release/symbol uploads and broad administration require dedicated tooling. Mixed-purpose catalog execution is unavailable to Research.
Linear Issues, projects, comments Exact read exceptions require authenticated discovery.
GitHub Repositories, issues, PRs, reviews, Actions Local Git and release uploads require separate tooling and authentication.

Before relying on a new connection, verify account identity, a representative read, an explicitly requested write, mutation denial in Research, and token refresh. The repository checks generated configuration and policy behavior; account entitlements and live upstream tool catalogs require this authenticated verification.

Executor

Limitless can host Executor on NixOS and configure its scoped connections on any OpenCode client. Executor owns upstream account grants; each client signs in to Executor separately. AI-provider authentication is independent and unchanged.

OpenCode clients ── private HTTPS ── Executor ── Notion / Atlassian / Sentry
  client grants                    upstream OAuth and persistent database

Server

Import limitless.nixosModules.executor and enable the service:

services.limitless.executor = {
  enable = true;
  origin = "https://server-athena.example.ts.net:9443";
};

The service uses a pinned official multi-platform container with rootful Podman, bridge networking, and a loopback-only port (4400 by default). It does not add a user to a privileged container group or open the host firewall. The consuming configuration owns private HTTPS routing and its access rules; use a dedicated Tailscale Serve port rather than changing existing routes.

The service enables Executor's official app catalog. Catalog apps declare their own authentication requirements and may use REST APIs rather than hosted MCP. For the existing Notion, Atlassian, and Sentry MCP accounts, choose Add app → Connect a service and enter the official MCP URL; this works independently of the catalog feed.

For first-owner setup, use origin = "http://localhost:4400" and leave the HTTPS route disabled. Forward the loopback port over SSH and create the owner in your browser. Verify /api/auth/self-host/config reports setup: false, then set the final HTTPS origin and enable its route. An empty instance's owner setup is unauthenticated; do not expose it to the whole tailnet before creating the owner.

Executor creates its database, OAuth encryption keys, auth key, and apps in /var/lib/limitless-executor/product, behind a root-owned 0700 parent directory. Podman uses a dedicated subordinate-ID range: its internal UID 1000 is not the agent's host UID 1000. The product and collector mounts get mapped ownership; never share these mounts with another service or manually reuse its ID range. Do not protect backups of its master keys with an encryption identity already readable by agent processes. The container is the credential boundary, not a sandbox for the machine's administrator. Never copy its database or upstream refresh tokens to clients or the Nix store.

Before upgrades, stop the service and back up the whole state directory to a root-only location, then restart it. Database and keys must be restored together. There is no automatic off-host backup or database downgrade guarantee. The collector cache is disposable; treat logs and traces as potentially sensitive.

Accounts and scopes

Create the owner yourself, then connect the official remote MCP services from the dashboard. One app per service can hold several accounts, such as Notion's work and personal workspaces; label each account and verify its workspace. For Atlassian, use https://mcp.atlassian.com/v2/mcp?tools=all and select the intended site. Do not replace these with REST integrations and assume their OAuth requirements match.

Dynamic client registration can use a private HTTPS callback when the browser is on the tailnet and the provider permits that redirect. Test each actual provider before migration. Do not enable CIMD with a private metadata URL that the provider cannot fetch. Use your own browser for account sign-in, not an agent-controlled browser.

For least privilege, create two scoped connections, excluding Executor's administrative apps:

  • Read/write: Limitless and Oracle use the intended accounts and tools, with model-handled elicitation.
  • Read: Research uses a connection configured for read-only access inside Executor. Browser elicitation is a fail-safe, not the read-only boundary.

No upstream read-tool list is duplicated in Nix for Executor mode. This trusts Executor's classification and enforcement, including its treatment of generic execution tools and future catalog changes. Verify that the configured policy rejects writes rather than relying on the connection's label. Connection IDs are non-secret; a connection URL is not authorization. Sign in through OAuth, not a personal access token, which bypasses scoped-connection restrictions.

Clients

After creating and verifying the scopes:

programs.limitless.mcp.executor = {
  enable = true;
  url = "https://server-athena.example.ts.net:9443/mcp";
  connection = "primary-connection-id";
  readConnection = "read-connection-id";
};

Alternatively, explicitly opt Limitless and Oracle into the broad endpoint:

programs.limitless.mcp.executor = {
  enable = true;
  url = "https://server-athena.example.ts.net:9443/mcp";
  fullAccess = true;
};

This grants Limitless and Oracle the full consented interface, including Executor administration and all authorized apps. Do not also set connection. Research remains denied the broad connection; an optional readConnection still requires a separate read-only scope. One endpoint or scoped connection can be shared across the fleet, but every OpenCode server authorizes its own grant. Do not synchronize OAuth token files between machines.

For a remote OpenCode host, set callbackPort = 19876 (and optionally readCallbackPort = 19877). The browser must reach that OpenCode host's loopback listener; OpenCode 2.1.4 does not relay callbacks through its frontend. Open an SSH forward from the browser computer before running /mcps against the remote backend:

ssh -N -o ExitOnForwardFailure=yes \
  -L 127.0.0.1:19876:127.0.0.1:19876 chase@server-athena

Forward 19877 too when onboarding executor-read. Leave the waiting sign-in dialog open until it reports Connected. No tunnel is needed when OpenCode runs on the browser computer, or for ordinary refresh after authorization. These ports are unrelated to Executor's upstream account callback. Do not set a custom redirect URI or forward the OpenCode service port as a substitute.

Run /mcps and sign in to executor and, when configured, executor-read on each device. Their tokens stay in OpenCode's mutable credential store. Each grant is bound to the exact MCP URL, which Limitless writes in Executor's canonical form: the connection parameter, and elicitation_mode only when it is not the default model. Any change to that URL, including the switch to canonical URLs, needs a new sign-in. With readConnection = null (the default), Research has no MCP tools in Executor mode. When supplied, Executor must enforce the read-only scope on every call and execute; Nix cannot verify the dashboard's mutable policy. Limitless and Oracle see only executor, and Research sees only tools from executor-read, including future wrappers whose authorization remains Executor's responsibility.

These are tool-selection rules, not a process sandbox: agents with shell access run as the same user and can reach that user's stored credentials. Preserve the instruction-level restrictions on external mutations. Test a known mutation's denial through both call and execute before granting Research access.

Executor exposes its own skills/search/describe/call/execute/resume interface, not the upstream catalogs. codemode = false avoids wrapping it in a second Code Mode layer. Upstream resources, prompts, session state, URL elicitation, and OpenCode session metadata are not transparently forwarded. Verify the workflows you need rather than treating this as a transparent MCP proxy.

Revoke a device under Connected agents; revoke a scoped connection to revoke all its device grants. Keep an onboarding record if clients display identical names. This is not per-project isolation. An unavailable Athena means unavailable gateway tools; native provider accounts and local tools continue independently. An Executor restart can lose paused executions. Inspect effects before retrying a mutation; never automatically replay an uncertain program.

Pilot acceptance

Keep direct MCP connections until all of these are verified:

  1. ARM64 startup, private routing, owner creation, and closed signup afterward.
  2. Both Notion identities, the intended Atlassian site, and Sentry scope through the exact callback origin.
  3. Main/Oracle reads and separately authorized writes; Research mutation denial for both direct calls and programs. Scoped connections exclude administration; an explicitly chosen fullAccess read/write connection for Limitless and Oracle intentionally includes it.
  4. Device revocation, reconnect, upstream refresh after expiry, and restart.
  5. Persistence/restore, inaccessible state for the agent user, and resource use.

Only then cut clients over one host at a time. Account authorization, mutable scope validation, credential expiry, and actual provider access need live checks; the Nix checks establish generated configuration and permission behavior only.

Configuration and layout

nix/modules/
├── home.nix       # composition and Git hygiene
├── opencode.nix   # runtime, agents, plugins, and configuration assembly
├── service.nix    # optional Linux supervision and native service binding
├── t3.nix         # optional headless T3 service and OpenCode provider binding
├── desktop.nix    # independent Linux desktop installation
├── darwin.nix     # macOS desktop installation through Homebrew
└── mcp.nix        # named connections and permission generation
nix/mcp-presets.nix # first-party endpoints and audited read-tool names
opencode/          # native defaults, agents, and shared instructions
packages/limitless/ # artifacts, source research, and client policies

Native server settings go in programs.limitless.opencode.settings. Limitless enforces the default agent, managed plugins, ordered permissions, generated MCP policies, and the managed-checkout edit denial. Managed plugin directives disable opencode.browser independently of agent and session permissions. Set ordered rules through opencode.permissions, not opencode.settings.permissions. Caller rules override the default edit allowance; managed-checkout edit and T3 namespace denials remain last. Managed configuration, instructions, agents, skills, and TUI plugins are installed under $XDG_CONFIG_HOME/opencode (normally ~/.config/opencode). opencode.extraAgentsFile appends additional instructions. opencode.disableClaudeCode = true wraps the executable with OPENCODE_DISABLE_CLAUDE_CODE=1.

Native terminal settings go in programs.limitless.opencode.cliSettings. The launcher supplies them through OPENCODE_CLI_CONFIG_CONTENT; declared values override matching preferences in ~/.config/opencode/cli.json. An explicitly supplied OPENCODE_CLI_CONFIG_CONTENT replaces the generated override. Apply Home Manager and relaunch the TUI after changing these Nix settings.

skills.package can supply your own skill directories; skills.enable = false disables installation. The default package copies a top-level skills/ directory when present and is otherwise empty. skills.extraDirectories adds directories of <name>/SKILL.md skills beside the package; a skill name defined twice fails the build. OpenCode's built-in skills remain available.

Behavior Model combines system probing and code-based state mapping in one skill. Its modeling procedure consolidates findings and compares revisions using a common format and review checks. Use the procedures the investigation needs; they contribute to the same model.

Agents and models

limitless is the primary user-facing agent and defaults to anthropic/claude-opus-5-5#xhigh. Connect Anthropic through /connect to make Opus available; OpenCode falls back to its default model when the agent's model is unavailable. Existing sessions retain their selected model. Select a model in OpenCode, or supply custom agent definitions through programs.limitless.agents.package. Agent Markdown model fields take precedence over settings in the same configuration directory, so opencode.settings.agents.limitless.model does not override the packaged default.

Research uses openai/gpt-6.1-sol#xhigh; Oracle uses openai/gpt-6-astra#xhigh. Subagents keep the processing tier of their configured model.

Limitless owns implementation, integration, and final validation. Design is collaborative; once approved, Limitless carries the work through review and delivery. Goals and explicit requirements stay with the user, while the implementation approach can improve through engineering judgment and Oracle consultation. Meaningful departures are explained in the PR. The commit-and-pr skill commits by repository convention, runs local checks, opens the PR, and watches CI and review comments, fixing failures until the PR is green.

Research finds relevant code, documentation, and external sources to answer focused questions. It returns findings and citations; Limitless owns decisions and implementation. Oracle diagnoses difficult problems, resolves technical uncertainty, advises on consequential architecture and maintainability, and reviews changes. Limitless can investigate directly or consult Oracle when independent judgment is useful. Limitless gives helpers the context they need. Oracle investigates directly and may recommend a separate investigation to Limitless; neither helper starts subagents. Substantive completed changes receive one fresh Oracle review, followed by same-session checks of fixes and open findings.

Oracle can run tests and make focused diagnostic edits directly in the shared workspace, including reproduction tests, fixtures, and temporary instrumentation. It reports changed files, their purpose, commands, results, and material interference. Limitless reviews the diagnostic changes and decides what to retain or remove; automatic cleanup is not required. Limitless waits when work could conflict or an investigation gates the next change, and otherwise works on independent tasks. Oracle accounts for shared files, services, and test resources and preserves concurrent work. Diagnostic scope and implementation ownership are instruction-level policies; edit permissions cannot distinguish those purposes.

Astra and its Fast mode use conservative client budgets of 872,000 context tokens and 828,400 input tokens, overriding OpenCode's 400K subscription default. These derive from the Codex catalog's override ceiling with 5% input headroom. These budgets have not been verified against the subscription backend. With OpenCode's default 10% compaction buffer, automatic compaction starts at approximately 745,560 input tokens. Reasoning variants, Fast mode, and output limits come from the upstream catalog.

OpenAI models use native provider compaction for automatic and manual requests by default. Configure providers.openai.settings.compaction.type through opencode.settings; individual model settings can override the provider policy.

providers.disabled defaults to [ "google-vertex" "google-vertex-anthropic" ] to avoid ambient Vertex selection.

Native delegation

Limitless uses OpenCode's native subagent tool for Research and Oracle. Follow-ups pass the same sessionID. The depth limit is one, and both helpers deny subagent calls:

Limitless (Opus xhigh)
├── Research (Sol xhigh)
└── Oracle (Astra xhigh)

Independent changes can use separate Limitless sessions in native worktrees; the repository-setup skill covers checkout setup and teardown.

OpenCode owns background-job completion and restart recovery. A known upstream limitation in 2.1.4 can lose an automatic result when a follow-up is admitted while the prior execution settles. Inspect the existing child before repeating an interrupted launch; recovery does not guarantee exactly-once execution or effects.

Anthropic subscription authentication

Run /connect, select Anthropic, and choose Claude Pro/Max. The plugin is pinned to ex-machina-co/opencode-anthropic-auth release v2.0.0-next.3, commit e03f023c8ea1771a829cee51024b8675ffede24c. API-key authentication retains standard Anthropic request behavior.

Warning

Anthropic does not officially support using Claude Pro/Max through OpenCode. This compatibility path may violate its terms or put an account at risk. Disable it with programs.limitless.plugins.anthropicAuth.enable = false.

Connecting Max replaces the saved Anthropic API-key credential and vice versa. Credentials from the earlier experimental Limitless plugin require signing in again. OpenCode continues to display API prices for subscription models. The upstream plugin targets SDK 2.0.4 and reports Claude Code compatibility version 2.1.280. When Anthropic returns a structured newer-version requirement, it adopts that exact minimum and permits one retry for the affected session, agent, and model. Ordinary errors and rate limits do not trigger this recovery.

An optional override is available when an explicit version is required:

programs.limitless.plugins.anthropicAuth.claudeCodeVersion = "2.1.280";

The option defaults to null, preserving the bundled version and automatic recovery. Setting a version supplies ANTHROPIC_CLAUDE_CODE_VERSION to both managed and on-demand OpenCode servers and disables automatic version adoption. An explicit environment value takes precedence. The plugin reads it at startup and uses it for both the user-agent and billing metadata; apply Home Manager and restart the OpenCode server after changing it. This setting controls the plugin's reported compatibility version, independently of any installed Claude Code executable.

Refresh rotation is deduplicated within a process. ANTHROPIC_BASE_URL can override the endpoint; ANTHROPIC_INSECURE cannot disable TLS verification.

Guarded source research

programs.limitless.github = {
  enable = true;
  allowedRepos = [ "owner/repo" ];
  tokenFile = "/run/agenix/github-read-token";
};

Without tokenFile, the tool reads tokenEnv, which defaults to GITHUB_TOKEN. Use a fine-grained read-only token for private repositories. The token is passed to Git through ephemeral github.com-scoped environment configuration and never written to URLs, arguments, tool results, or repository configuration.

allowedRepos must be nonempty unless allowUnrestrictedRepos = true. The policy also covers every transitive submodule; non-GitHub hosts are rejected. Clones are depth-one, project-local snapshots. Default-branch requests refresh immediately; explicit branches, tags, and SHAs use distinct deterministic paths. Dirty checkouts are never overwritten. New clones are staged and published atomically. LFS objects are not materialized.

Managed repositories are read-only supporting source. The edit policy denies normal writes below .limitless/repos/; shell access is not sandboxed. Set git.ignoreStorage = false if the repository should manage .limitless/ itself.

Artifacts

artifact_create creates a project-local folder under .limitless/artifacts/ with a manifest recording its slug, optional title, timestamp, and creating session. Use normal file tools to add notes, source, assets, or generated outputs. artifact_list reports valid artifacts and folders with invalid/missing manifests. Explicit duplicate slugs fail; generated slugs include a random component. Failed creation cleans up only its own incomplete manifest and empty folder.

Native service and Tailscale

OpenCode starts its native background service on demand by default. Use opencode service status and opencode service restart to inspect and manage it.

For automatic startup at user login and process supervision on Linux, enable the Home Manager systemd user service:

programs.limitless.opencode.service = {
  enable = true;
  hostname = "127.0.0.1";
  port = 4096;
};

hostname defaults to 127.0.0.1; port defaults to OpenCode's native 49374. Declare 4096 explicitly to retain an existing Tailscale proxy target:

Tailscale HTTPS → 127.0.0.1:4096 → OpenCode

Reuse the declared port in your proxy configuration. The listener stays on loopback, and OpenCode's native authentication still applies through the proxy.

The service persists hostname and port through native opencode service commands when they differ, stops any existing native daemon, then runs serve --service in the foreground. It preserves the private password, CORS configuration, and configured service environment. Private service files remain writable and outside the Nix store. Disabling supervision leaves the last persisted binding in place.

With mcp.executor enabled, the unit first waits up to about 60 seconds for the Executor endpoint to answer HTTP (any status below 500, normally 401). Tailnet DNS and the gateway can come up after user services at boot, and OpenCode does not retry an MCP connection that failed at startup. It starts anyway after the timeout.

Enabling, starting, or restarting this unit can interrupt active work and persistent terminals. The unit takes ownership of the shared native daemon. While enabled, use systemd for lifecycle commands:

systemctl --user status opencode
systemctl --user restart opencode
systemctl --user stop opencode

opencode service stop is normally undone by supervision. A local OpenCode client can still start a native daemon after the unit is stopped. Keep local clients on the configured OpenCode version; clients that replace the daemon with another version can conflict with supervision. Restarting a failed process is automatic; this unit does not detect a live but unhealthy process.

Home Manager applies changed units automatically when systemd.user.startServices is enabled. Changes to the runtime, binding, configuration, agents, or installed skills trigger a restart. With service switching disabled, apply the suggested systemd commands yourself.

The unit starts with the systemd user manager, normally at login. Boot startup is optional: user lingering starts that manager before login and keeps it running after logout. To opt in, set this in your NixOS system configuration:

users.users."your-user".linger = true;

On other systemd distributions, an administrator can enable lingering with loginctl enable-linger <your-user>. Tailscale's proxy must also start at boot.

Desktop clients

Limitless installs OpenCode Desktop and T3 Code through independent flags:

programs.limitless = {
  opencode.desktop.enable = true;
  t3.desktop.enable = true;
};

On x86_64 Linux, import limitless.homeModules.default into Home Manager. The flags install pinned AppImages, also exported as packages.opencode-desktop and packages.t3-desktop. Each has a desktop.package override. Desktop-only use does not require programs.limitless.enable and does not provision agents, configuration or background services.

On macOS, import limitless.darwinModules.default into nix-darwin and set the same flags in the system configuration. They install the native Homebrew casks opencode-desktop and t3-code. Homebrew must already be installed. The module enables Homebrew by default when a desktop is requested, but leaves its upgrade and cleanup policy to the host. Cask versions follow Homebrew rather than the Linux pins, and apps may update themselves. Use cleanup "uninstall", not "zap", to preserve app data when removing or switching clients.

Linux Home Manager flags → pinned desktop packages
macOS nix-darwin flags   → native Homebrew casks

OpenCode Desktop and the shared service

The Linux desktop is pinned to the Limitless OpenCode release (packages.opencode). When the Limitless workspace is enabled, module assertions reject a desktop/CLI version mismatch: Desktop can stop and replace a registered local service at startup when its bundled CLI version differs. Other, independently installed CLIs are outside this check.

With the matching managed service healthy, Desktop adopts that service. If no healthy service exists, it starts its bundled CLI, without the Limitless launcher environment; systemd may then take ownership again and interrupt work. Start the managed service before opening Desktop and close Desktop before runtime upgrades. Selecting a remote server in Desktop does not disable local service startup. On Macs without a configured local Limitless workspace, this local service uses ordinary OpenCode configuration. Add remote servers through Desktop's pairing UI.

T3 desktop installation likewise does not configure its local backend settings. Follow the section below when attaching it to a managed Linux service. Its Linux package ships with a matching server version; an independently updated client needs compatible protocols and feature support. Home Manager restarts T3 on changed units when service switching is enabled.

T3 Code

Use the packaged opencode-t3 launcher to connect T3 to the existing managed OpenCode service. It starts only a loopback proxy, never another OpenCode server. OpenCode's native subagent tool owns Research and Oracle delegation and preserves native child sessions and events. Only Limitless delegates; Research and Oracle cannot delegate further. Resume an investigation with its sessionID.

Setup

On Linux, let Home Manager run T3 independently of its desktop GUI:

programs.limitless = {
  enable = true;
  opencode.service.enable = true;
  t3.enable = true;
};
T3 desktop / web → t3code.service → opencode-t3 → opencode.service

The t3code user service listens on 127.0.0.1:3773 and uses the existing ~/.t3 home, preserving threads and accounts. Options t3.port, t3.baseDir, and t3.package override these defaults. Enable t3.desktop.enable for the GUI. Linux user lingering and a private HTTPS proxy belong in the host configuration, as described above; T3's own Tailscale Serve management is disabled.

For NixOS browser previews and HTML rendering, enable programs.nix-ld.enable = true in the system configuration. The T3 package supplies its downloaded Chrome's libraries through a nix-ld path inherited by T3's process tree, including terminals and provider CLIs. It preserves inherited library paths and the host's loader without introducing an architecture override. It does not add global libraries or set LD_LIBRARY_PATH. Keep Limitless's nixpkgs input following the host's input so the browser libraries and host loader stay compatible. Chrome's sandbox remains enabled; the host must support unprivileged user namespaces. T3's suggested sudo t3 browser setup uses apt and is not the NixOS installation path.

Before the first switch, finish active work, back up your T3 home, and close the desktop's local environment. On startup, the service enables the default OpenCode provider, points it to the absolute opencode-t3 profile path, and clears any remote Server URL. It also sets localEnvironmentEnabled = false for the desktop sharing that T3 home. Reopen the desktop and add http://127.0.0.1:3773 as a remote environment, using a pairing link from t3 pair --base-dir "$HOME/.t3". Pairing credentials remain runtime state, never Nix configuration. A desktop on another machine must have its local environment disabled separately if desired.

Only those settings are managed, at service startup rather than activation. Unrelated fields and file modes are preserved; settings must be regular files containing plain JSON. JSONC, symlinks, malformed data, or a live server PID block startup instead of being overwritten. t3.opencode.enable = false stops managing the provider binding without undoing existing settings. Disabling the module does not revert the mutable desktop or provider settings.

After an unclean shutdown, PID reuse can leave a stale runtime record blocking startup. The error identifies the PID and file. Stop retries with systemctl --user stop t3code, close any desktop backend, and inspect that PID. Only after confirming the record is stale and no T3 backend uses this home, move userdata/server-runtime.json aside and start t3code again. Do not kill an unrelated process or remove settings or databases. Timestamps are not used to bypass this guard because clock changes can make a live process look stale.

With systemd.user.startServices = true, Home Manager automatically restarts T3 when its package or service configuration changes during a switch. Active clients may briefly disconnect. OpenCode configuration changes can also restart OpenCode during a switch. Do not use t3 service install or t3 service update alongside Home Manager: they own the same unit. T3 requests OpenCode startup when both units are enabled, but stopping either does not stop the other. With external OpenCode supervision, ensure that service is available.

For an independently managed T3 desktop or server:

  1. Finish or stop active work before applying Home Manager or restarting OpenCode; a service restart can interrupt sessions. Load the updated plugin with systemctl --user restart opencode when supervised, or opencode service restart otherwise. Check the endpoint with opencode service status.
  2. In T3 Settings → Providers → OpenCode, set Binary path to opencode-t3 and leave Server URL blank. If T3 does not inherit your PATH, use the absolute path printed by command -v opencode-t3. Reconnect the provider.

Leaving Server URL blank lets T3 attach its thread-scoped MCP credentials, including browser access. The launcher discovers and authenticates to the existing service; no password needs copying into T3 settings. Other CLI use still goes through opencode.

Behavior

All agents lose T3's delegate_task, task_status, and task_cancel, plus the broken HTML visual tools html_preview and html_render. The primary agent (limitless) retains T3's other tools, including top-level thread creation and management. Research and Oracle receive no T3 tools, including browser tools and future additions. They are hidden from Code Mode and search; there is no execution-only deny hook. Subagents return findings and blockers through their native results. By default, unbound TUI/CLI sessions do not receive thread-bound tools.

The plugin removes the blanket session allow that T3 Full access writes, which would otherwise erase native role restrictions. Native configuration remains authoritative, with T3's operator asks, Plan restrictions and specific grants preserved. The bridge forwards T3's permission writes unchanged; the plugin repairs each one from OpenCode's session.created and session.permissions events, in commit order, and again before every prompt, including a native child's first. Without the Limitless plugin, T3's rules stay as written, and native children inherit them. Normal mode selects the actual configured default agent. Limitless ships with the built-in Plan agent disabled: T3 Plan turns fail with agent-not-found unless a Plan agent is configured. Returning to normal mode restores the default agent.

Only T3's named t3-code instruction entry is replaced, with a neutral marker before persistence. The plugin expands that marker into T3 guidance for primary agents when preparing model requests. Research and Oracle receive no T3 guidance, and any inherited marker is removed from their outgoing requests. The primary template lives in packages/limitless/t3/prompts/; the base agent Markdown files are unchanged. Ordinary sessions receive no T3 guidance. Other context, conversation content, and subagent events pass through unchanged. Existing provider history is not retroactively erased; use a fresh thread for the cleanest instruction cutover.

Closing or reconnecting T3 only detaches the bridge. Native sessions and background children keep running, and can continue consuming tokens and changing files. Use explicit Stop before disconnecting when cancellation is wanted. T3's MCP cleanup calls pass through; browser/app capabilities may be unavailable while disconnected. The bridge never interrupts sessions or stops the service.

Compatibility boundary

The bridge (packages/limitless/t3/{cli,bridge,rewrites}.ts over packages/limitless/bridge/) is an Effect loopback proxy. It authenticates T3, forwards HTTP and server-sent events to the managed service with that service's credentials, and rewrites three requests: the t3-code instruction entry, T3 MCP server names, and normal mode's build agent. It rejects WebSocket upgrades, which T3's OpenCode provider does not use. Permission policy lives only in the plugin (t3/{plugin,session-policy,permissions}.ts): between T3's write and the plugin's repair, a running session keeps T3's rules, and the repair has no latency bound. Repairs follow the order of T3's writes; a plugin reload or session move in the middle of one can leave an earlier write's rules, still in repaired form, until T3 emits another permission write; turns that change nothing emit none. This is not a shell or HTTP sandbox.

The t3-code and t3-code-* connection names are reserved; use t3-external for independent T3 OAuth connections. Underscore-containing thread namespaces, such as a fork's <thread>_fork, receive stable t3-code-<32 hex> aliases so prefix-related names cannot grant sibling tools.

This adapter targets OpenCode 2.2.5 and upstream stable T3 0.1.6 on both x86_64 and ARM Linux. T3's OpenCode 2 provider still selects build for normal mode, which the bridge maps to the configured default agent. The per-thread agent picker is not available on this V2 path. Keep the integration checks with runtime upgrades.

Notifications

Limitless enables sound by default and supplies a TUI handler that dings for:

  • A newly completed final assistant response in a top-level session.
  • A permission request.
  • A question or other input prompt.

Completion requires successful execution, a normally finished answer, and an empty inbox. Child completion, errors, interruptions, and compaction-only runs are silent. A later background result can resume the main session and produce another response. The handler uses OpenCode's native sound playback and preserves visual notifications and error toasts. Configure sound, volume, and visual notifications through the native CLI settings:

programs.limitless.opencode.cliSettings.attention = {
  sound = true;
  volume = 0.4;
  notifications = true;
};

Set opencode.cliSettings.attention.sound = false to disable the ding. Sound and visual notifications are independent; visual notifications normally appear when the terminal is unfocused. Preferences omitted from cliSettings can be changed through the TUI and stored in ~/.config/opencode/cli.json.

The package's ./tui entry replaces the pinned built-in opencode.notifications handler by ID. Existing -opencode.notifications directives also disable this handler. OpenCode loads the TUI entry automatically from the Limitless plugin.

Prompt-cache status

A dot in the prompt footer estimates whether the provider still holds the session's prompt cache:

Color Meaning
Green A request is running, or the latest reply finished within the cache lifetime.
Red The cache lifetime has elapsed since the latest reply; the next request likely rewrites it.
Gray The provider's cache lifetime is unknown.

Lifetimes are 5 minutes for anthropic (OpenCode sends only 5-minute cache breakpoints) and 30 minutes for openai (GPT-5.6 and later guarantee at least 30 minutes). The dot is a timer estimate: warming requests are invisible to the TUI, and providers can evict early.

The plugin is TUI-only and ships as Solid source that OpenCode compiles at load. Home Manager links it into $XDG_CONFIG_HOME/opencode/plugins/limitless-cache-status (normally ~/.config/opencode/plugins/limitless-cache-status). Disable it with programs.limitless.plugins.limitless.cacheStatus.enable = false.

Migration from the previous Limitless layout

This release removes these options and packages:

Removed Migration
paseo.*, paseo.desktop.enable, opencode-paseo, paseo_* tools, packages.paseo and packages.paseo-desktop Remove programs.limitless.paseo settings and any Paseo hostname or proxy configuration. Use T3 Code or the OpenCode TUI with the managed service. The Paseo daemon's data in ~/.paseo is left in place.
lsp.*, bundled language servers, ast_grep_* and lsp_* tools Remove programs.limitless.lsp settings. Install any language servers or TypeScript needed on PATH through home.packages. Use project tooling or an independently installed ast-grep CLI through the shell.
oracle-design and oracle-solve agents Use oracle and move any opencode.settings.agents overrides to that ID; the module rejects retired IDs. Update custom instructions and remove stale agent files in manual installations. Finish active consultations before applying. To continue an existing consultation, pass agent: "oracle" with its existing sessionID from the parent. Oracle no longer delegates to Research. Limitless now defaults to Opus xhigh; existing sessions retain their selected model.
lead and unit agents, automatic Unit worktree placement Finish active Lead/Unit runs before upgrading, then select limitless to continue existing sessions. Use separate Limitless sessions for independent changes and native subagents for Research and Oracle. Existing sessions and worktrees remain available.
slack.*, Gary, Slack tools Remove Slack configuration and retire its bot credentials when no longer used.
desktop.enable Use programs.limitless.opencode.desktop.enable. The opencode-desktop package is available again as the upstream V2 client; it does not restore the old patched launcher.
tools.agentBrowser.*, agent-browser package/skill Use T3 Code's browser tools. OpenCode's native browser catalog is disabled.
tools.acli.*, Atlassian CLI skill Add an atlassian MCP connection and authorize it.
tools.notion.*, notion-cli package/skill Add separately named notion MCP connections for each workspace.
tools.sentry.*, sentry package/skill Add a sentry MCP connection. Install specialized release tooling separately if required.
mcp.linear.enable Set mcp.servers.linear.preset = "linear" and sign in through /mcps.
notifications.* Use opencode.cliSettings.attention; sound defaults to enabled, and visual notifications are configured independently. Arbitrary command hooks are retired.
opencode.service.alias and its attach alias Use native OpenCode service discovery. Optional Linux supervision uses opencode.service.{enable,hostname,port}.
pull-request skill Use commit-and-pr, and update any instructions that name the old skill.
limitless-fast agent, agents.fastSubagents Use limitless. Select a -fast model alias to request Fast processing for a session.

If migrating from the old systemd service, remove opencode.service.alias and declare port = 4096 to preserve its old default. Applying the generation with supervision enabled replaces the old unit and takes over any native daemon. If using native on-demand startup instead, disable the unit before launching OpenCode. Do not keep a separate OpenCode supervisor alongside this unit. Applying these settings is a Home Manager operation; repository checks use isolated fixtures.

For an existing Linear API key, the connection's native settings can explicitly set oauth = false and headers.Authorization = "Bearer {env:LINEAR_API_KEY}". Default connections use OAuth. Former CLI token files are not imported into MCP authentication; authorize and verify each new connection before retiring tokens.

OpenCode 1 sessions are not migrated. Back up OpenCode state and record your flake revision before switching major versions. Rollback requires the matching runtime and state backup; never run V1 and V2 against the same writable state directory.

Development

See AGENTS.md for repository-local architecture, code style, and review guidance. Use nix develop, then bun install --frozen-lockfile and bun run ci. The gate runs formatting/lint checks, TypeScript, tests, module checks, and all five package builds. The suite covers Limitless-owned behavior: tool safety and lifecycle, role permission outcomes, MCP namespace collisions, launcher environment handling, and native service supervision. It leaves configuration values and upstream packages to their owners.

The Linux t3-browser check verifies the actual package wrapper's environment and the pinned browser's loader dependencies. Run its sandboxed browser smoke test outside the Nix build sandbox on each NixOS host:

nix run .#checks.x86_64-linux.t3-browser
# On ARM Linux, use checks.aarch64-linux.t3-browser instead.

It exercises isolated shared and persistent browser contexts plus direct HTML rendering, using temporary profiles and no live T3 state or external websites.

Runtime, Limitless plugin SDK, and schema are pinned to 2.2.5, with effect@4.0.6; update them together. Hosted plugins use OpenCode's shared Effect and plugin runtime; the standalone T3 bridge bundles its own dependencies. The cache-status plugin's type-only dev dependencies (@opencode/theme, @opentui/*, solid-js) follow the same OpenCode release: 2.2.5, 0.8.0, and 1.9.17, respectively. The @effect/platform-node-shared override keeps the standalone bridge on Effect's matching 4.0.6 platform runtime instead of resolving a newer, incompatible peer requirement. Re-audit native capabilities and MCP read exceptions when upgrading. Vendor references for the current allowlists are linked in the integration table above.

When upgrading OpenCode or T3, update the matching *-desktop.nix hash and build .#opencode-desktop and .#t3-desktop. The x86_64 Linux flake checks build both to catch stale desktop pins before deployment.

When upgrading OpenCode, verify that the TUI still replaces a built-in handler with a later external definition of the same ID, that the built-in notifications ID is still opencode.notifications, and that execution events still project matching idle-message boundaries. The sound filter depends on these pinned behaviors.

About

An opinionated OpenCode setup powered by Nix.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages