AI in Railbase
What the built-in AI engine is, where it runs, and the rules it follows.
Updated
Railbase has an AI Engine built into the core binary. It is not a plugin, not a separate service, and not a paid add-on: there is no AI licence, no token quota, and no AI line on your bill. What Railbase does not ship is a model — you bring the compute. Until you connect one, the AI features simply aren't there, and nothing else about your instance changes.
The engine is the part in between: it decides which model a call goes to,
enforces your egress rules, redacts before anything leaves the process, and
writes a redacted audit row for every generation. Plugins reach it through one
platform binding ($app.ai) and never talk to a model themselves.
Bring your own model
You connect a model in the admin console under Settings → AI, on the LLM tab. There are exactly three kinds of provider.
| Kind | What it is | Where the work happens |
|---|---|---|
external.cloud |
A cloud provider reached with your own API key (BYOK) | The provider's servers |
local.endpoint |
An OpenAI-compatible server you already run | Your machine (or LAN) |
local.native |
A model file Railbase loads inside its own process | Your machine |
external.cloud — today the external provider is Anthropic; the field is
fixed, and you supply your own API key plus an optional model name (left empty,
claude-sonnet-5 is used). You pay the provider directly — Railbase never meters
or bills the tokens.
local.endpoint — any server that speaks the OpenAI chat shape at
/v1/chat/completions. You give it a base URL and (optionally) a model name;
loopback and LAN addresses are allowed here, unlike the outbound client used for
cloud providers. Railbase also probes the well-known local ports and shows
what's live under Detected local endpoints, with a one-click Connect —
Ollama on 11434, LM Studio on 1234, and on macOS an Apple Foundation Models
bridge on 11435 that exposes the Mac's built-in on-device model through the
same OpenAI-compatible shape. Nothing is auto-connected; detection only lists
candidates.
local.native — Railbase itself is the runtime. The Local models panel
installs two artifacts: a native inference library for your platform, and the
weights. Each is streamed straight to disk and rejected unless its SHA-256
matches, and the model is loaded on first use inside the Railbase process, so
no network is involved at inference time. The This host panel reads your
actual CPUs, RAM, free disk and GPU and reports the largest model tier this
machine can run. With nothing installed, local.native reports itself
unavailable rather than failing calls hard — as it does on any platform other
than macOS, the one its in-process runtime is verified on today.
Note
Not every backend can do everything. A chat model can generate; embeddings are
a separate capability, and Railbase routes per capability instead of pretending
one model does it all. Anthropic and Apple Foundation Models have no embeddings
endpoint, and the current local.native slice ships no embedding weights — so
$app.ai.embed reports no-embedding-backend on those. Document search and
the assistants keep working regardless: with no embeddings backend the engine
falls back to a built-in lexical index.
How keys are stored
Provider credentials live in a core-owned collection (_ai_providers) that
only the engine reads. A plugin cannot reach them through its own secrets
binding, and the key is never sent back to any client: the admin screens show
only whether a key is set plus a last-four hint. There is no reveal and no
in-place key edit: the connect form always adds an entry, so replacing a key
means deleting the old one and connecting again. Each provider has a Test
button that runs one real, minimal call so you can prove the key works before
relying on it.
Everything is tenant-scoped
Providers, policies, the AI's own account, audit rows, usage counters and assistant conversations are all scoped to a tenant. An admin session has no tenant, so what an operator configures in Settings → AI is the site-level default; a tenant that has configured nothing of its own inherits it. A tenant that configures its own provider uses that instead.
The AI acts as itself, never as you
The AI does not borrow a user's identity. Each tenant gets exactly one AI agent
account (_ai_agents), created lazily the first time that agent is used, and
it is a first-class principal like a user or an admin. When the agent runs a
declared plugin action, the call goes through the real request pipeline —
same authentication, same licence check, same RBAC, same audit — under that
principal. Consequences worth knowing:
- It has no permissions by default. On the Agents tab you grant the agent roles, exactly as you would a person. A permission-gated action is refused until a granted role carries that permission.
- Every AI action is attributable. Audit rows carry the actor type
aiand the agent's id, so an AI-made change is never indistinguishable from a human-made one. - You can pause it. Disabling the agent stops all agent actions while keeping the account and its role grants intact.
What leaves your server, and when
Local backends never cross a trust boundary: a loopback endpoint or an
in-process model receives exactly the text your plugin wrote. For an
external.cloud provider, the engine applies a fail-closed gate before the call:
- External model egress is a master switch on the Policies tab. Turn it off and no external call happens, even with a provider configured.
- Sensitivity ceiling caps the data class allowed out — the default is
normal.restrictednever leaves, and anything unclassified is blocked rather than assumed safe. - Provider governance blocks a whole provider by name for a scope without deleting its configuration.
- Autonomy overrides are tighten-only: you can demote a declared action toward confirmation, never lift a gate away.
- A secret floor scrubs the prompt and system text on the way out: bearer
tokens, vendor API-key shapes, JWTs, passwords inside URLs, and any
key: valuefragment whose key counts as a credential.
A plugin also declares its own AI needs in its bundle manifest. If it declares
external_allowed: false, its calls are routed to local backends only — and on
an external-only instance they are denied outright rather than quietly sent out.
Where you'll actually see it
The floating assistant — the round button in the bottom-right corner of every admin page — answers questions about Railbase and your installed plugins from indexed docs with citations (and says so when nothing matches rather than guessing). The product documentation embedded in the core is indexed for it automatically on first start, so it answers out of the box.
The admin AI console — Settings → AI — is the operator surface: LLM, Policies, Usage (token counters, for observability — not billing), Tasks for async jobs, Agents, Docs, Memory, Peers, Delegations, Deliberations, Approvals for steps that paused for a human, and Audit with an XLSX export.
The site assistant — signed-in users get Assistant in the site navigation. It answers from the site-audience documentation of your installed apps, scoped to their tenant, with sources listed. Conversations belong to the individual user; another user's thread is simply not found.
Plugin capabilities — plugins call $app.ai for generation, structured JSON
output, embeddings, document search and declared actions. Whatever provider you
connected is what they use, under the policies you set. Nothing about a plugin's
price or licence changes because it uses AI.
If you configure nothing
This is the default state, and it is a supported one. With no provider
configured, every AI call returns no-provider-configured, the admin console
shows the engine as unconfigured, and the site assistant answers with a plain
"service unavailable". Records, plugins, jobs, auth and the rest of Railbase
behave exactly as they always did. Connecting a provider is the opt-in;
disconnecting it puts you back here.