← All Modules

assay.neutron

Neutron self-hosted agent platform — the full admin API from Lua: agents (personas, tool policies, guardrails, baked assay modules), secrets, git-host connections, workspace/guide resources, roles, instance settings, API tokens, usage. One client per instance; manage a fleet by creating several. Client: neutron.client(url?, opts?) — url defaults to env NEUTRON_URL; opts.token to env NEUTRON_TOKEN (a bearer minted in the instance's Settings → API, or its NEUTRON_BOOTSTRAP_TOKEN on first boot). Instances behind Cloudflare Access also send a service token: opts.cf_client_id / opts.cf_client_secret (env CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET).

opts.headers adds arbitrary headers to every request — an in-pod procedure running as the instance's internal principal sends x-neutron-internal and x-neutron-token beside its bearer rather than instead of it. When opts.headers is absent the client reads NEUTRON_EXTRA_HEADERS, a JSON object of header name to value; anything that is not such an object is an error rather than a silently unauthenticated call. Neither source can replace the Authorization header.

Agent config fields (all optional; whole-collection fields REPLACE — read current values first and merge): identity, mode, tool_policy, approver_roles, approver_users, capabilities, default_model, allow_user_switch, agentkit{enabled,police}, resources[ids], approval_timeout_minutes, inherit_persona, assay_modules, admin_only.

Raw calls

CRM

Campaigns:

People:

Inbox, sends and replies:

Sending estate:

Integrations, money and evidence:

Work tracker

local neutron = require("assay.neutron")

-- take over a newborn instance with its bootstrap token, mint a real one
local boot = neutron.client("https://agent.example.com", { token = env.get("BOOTSTRAP") })
local real = boot.tokens:mint("fleet-ops") -- bootstrap token goes dead after this

-- configure it end to end with the real token
local c = neutron.client("https://agent.example.com", { token = real.token })
c.connections:set("gitlab-bot", { kind = "gitlab", token = env.get("BOT_PAT") })
c.agents:create("Reviewer", {
  mode = "approval-gated",
  assay_modules = { "assay.gitlab", "assay.k8s" },
})

A procedure running inside the instance talks to itself over localhost, and reports a non-2xx rather than dying on it:

local neutron = require("assay.neutron")
local c = neutron.client() -- NEUTRON_URL + NEUTRON_TOKEN

local held, status = c.crm.sends:pending({ campaign = "alpha" })
if status ~= 200 then
  print("held queue unavailable: " .. status)
  return
end
for _, send in ipairs(held.sends or {}) do
  print(send.id .. " -> " .. (send.person or "?"))
end

Not mirrored: GET /api/admin/crm/people/:id/photo returns an image rather than JSON, so it does not fit this client's JSON-in, JSON-out contract. Reach it with http.get and the same headers, or through c:request when only its status matters.