npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@use-tusk/opencode-fence

v0.1.0

Published

OpenCode plugin that routes bash tool invocations through Fence for command-policy enforcement.

Readme

Fence Sandbox Plugin for OpenCode

OpenCode plugin that routes the bash tool through Fence's pre-tool-use hook for command-policy enforcement.

Fence is a lightweight, container-free process sandbox for running commands with network, filesystem, and command policies.

What it does

When OpenCode's agent calls the bash tool, this plugin intercepts the call and asks Fence to evaluate the command against your policy. Three outcomes:

| Fence verdict | Plugin behavior | |---|---| | Deny | Throws an Error with Fence's reason. OpenCode surfaces the error in the UI and the command does not run. | | Wrap | Rewrites the command to run inside fence -c "...". The command then inherits Fence's filesystem and network policy in addition to the command-policy check it just passed. | | Allow | Leaves the command unchanged. |

Why use this

Fence's command policy is enforced at two points:

  1. Preflight — once, on whatever command is given to fence (e.g. fence -- opencode only preflights opencode).
  2. Runtime exec — at the kernel exec boundary, against descendant processes.

Runtime exec on macOS, and Linux without runtimeExecPolicy: "argv", only handles single-token denies (e.g. sudo). Multi-token rules like gh repo create, git push, and npm publish are preflight-only — so when OpenCode spawns one of those after Fence has already preflighted opencode, the deny rule does not fire.

This plugin closes that gap by re-running preflight on every shell invocation OpenCode's agent issues, before the command runs. See Fence's Enforcement Across Child Processes for the full model.

Installation

Prerequisite

Install Fence and confirm it's on your PATH:

fence --version

You need Fence v0.1.52 or later for the --opencode-pre-tool-use subcommand the plugin spawns.

Add the plugin to OpenCode

The easiest way is to let Fence install it for you:

fence hooks install --opencode

This adds @use-tusk/opencode-fence to your ~/.config/opencode/opencode.json (or opencode.jsonc if you use that). See fence hooks install --opencode for details and flags (e.g. --file to target a project-local config).

If you'd rather edit the config file by hand:

// ~/.config/opencode/opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["@use-tusk/opencode-fence"]
}

OpenCode installs the package via Bun and loads the plugin automatically on next launch.

Configuration

The package's default export is a Plugin configured with sensible defaults — it is what gets loaded when you list @use-tusk/opencode-fence in opencode.json's plugin array.

To customize behavior (custom fence binary, pinned settings file, or template), import the factory from the /factory sub-path and construct the plugin yourself in a local plugin shim:

// ~/.config/opencode/plugins/fence.ts
import { createFencePlugin } from "@use-tusk/opencode-fence/factory";

export const Fence = createFencePlugin({
  // Pin a specific config file (mutually exclusive with `template`).
  settingsPath: "/Users/me/work/fence.json",

  // Or pin a built-in Fence template (mutually exclusive with `settingsPath`).
  // template: "code",

  // Override the fence binary location (default: "fence" on PATH).
  // fenceBinary: "/opt/homebrew/bin/fence",

  // For local development only. In production this turns the plugin into a no-op
  // when fence is missing or crashes; leave false.
  // failOpenOnRunnerError: false,
});

If you use the local-shim form, remove @use-tusk/opencode-fence from opencode.json's plugin array to avoid registering the plugin twice. The shim file is auto-loaded by OpenCode; the array entry would also load the no-options version.

The factory and the supporting types live on a sub-path so they don't appear in the main entry's exports — OpenCode's plugin loader iterates every export of a package's entry module and tries to register each one as a plugin, so anything other than the Plugin itself must live elsewhere.

If neither settingsPath nor template is set, Fence resolves config from the working directory the same way it does for direct invocations: walking upward for fence.jsonc/fence.json, falling back to ~/.config/fence/.

Composing with whole-agent wrapping

You can (and should) run OpenCode itself under Fence:

fence -t code -- opencode

…and additionally enable this plugin. The plugin detects FENCE_SANDBOX=1 (set by Fence on the wrapped process) and stops adding fence -c wrappers, so commands aren't double-sandboxed. The deny check still runs, which is the whole point of using both: whole-agent wrapping handles filesystem and network policy; the plugin handles multi-token command policy on platforms where Fence's runtime exec deny can't.

Limitations

User-typed ! commands bypass this plugin

OpenCode's plugin lifecycle does not currently fire tool.execute.before for commands typed directly into the TUI with the ! prefix. Those commands still run, just outside this plugin's reach.

If you need user-typed ! commands to also be policy-checked today, run OpenCode under Fence (fence -t code -- opencode). That gives you Fence's whole-process sandbox for all descendants, with the documented caveat that multi-token denies (which this plugin would catch) remain preflight-only for the ! path.

Spawn cost per bash call

The plugin spawns fence once per bash tool call. Fence's preflight is fast (sub-100ms in our benchmarks), but cold OS-level process spawn cost adds up if your agent is hammering bash. If this becomes a problem in practice, let us know.

Contributing

See CONTRIBUTING.md for setup, local-development install patterns, and the release process.

Bug reports, feature requests, and PRs welcome.