Railbase
GPTClaude

AI privacy and control

Run AI on your own hardware, control what may leave, and audit every AI action.

Updated

Most AI features are a data-export decision in disguise: your documents and prompts go to someone else's servers, and you find out afterwards. Railbase is built the other way round. The AI engine is part of the core binary on your server, it can run a model on your own hardware, and every route to an outside model is one you switch on deliberately.

The AI can run entirely on your hardware

Two ways to run a model locally, both keeping the text on the machine.

A local model server. Point Railbase at any OpenAI-compatible server on loopback or your LAN — Ollama, LM Studio, a llama.cpp server, or the bridge to a Mac's built-in foundation model. In Settings → AI → LLM Railbase probes well-known loopback ports and lists what it found under Detected local endpoints, so a running server is one click to connect. Nothing is connected for you. This is a provider of kind local.endpoint, and its address is pinned: it must resolve to a loopback or private address. One pointed at a public address is refused, so it can never quietly become a route to a cloud model.

A model file executed inside the core process. Railbase can load a native inference library and a .gguf weights file and run generation in its own process — no subprocess, no port, no network call at all. The Local models (local.native) card on the same screen installs both; the download streams into a content-addressed store and is checked fail-closed against the SHA-256 you enter from the signed catalog entry, so a tampered or truncated file leaves nothing installed — and an install with no hash is refused outright. The This host card shows this machine's CPU, RAM, free disk and GPU, and the model tier it can run.

Note

The in-process runtime ships verified for macOS; on other platforms — and anywhere the library or weights aren't installed — it reports itself unavailable as a typed, degraded state the AI screen surfaces, rather than failing calls hard. A local model server is the portable local option today.

Secrets are stripped on both boundaries

Redaction runs at two separate places, and neither depends on the other.

Before anything is written to the audit trail. The AI audit writer redacts at the single point where rows are created, whatever a caller passes in: it masks Authorization-style values, vendor API-key shapes (sk-…, AKIA…, ghp_…, xox…), JWTs, passwords inside URLs, and any key: value fragment whose key counts as sensitive. Structured context attached to a row is walked by key name and blanked the same way. The reason is blunt: the trail is append-only and tamper-evident, so a secret written into it could never be scrubbed out later.

Before anything is sent to an external provider. The same text-level filter runs on the prompt and system text on the way out to a cloud model, so what the audit row stores is what actually left your process. Local providers get the text unchanged — a loopback endpoint and the in-process model never cross a trust boundary, so their input passes through byte-identical.

On top of that, a payload carries a sensitivity class. restricted never goes to an external model; confidential goes only if you raised the ceiling. An unrecognised or unresolved class is blocked, not treated as ordinary data — a false positive costs you a call, a false negative costs you a leak.

What you control as an operator

Settings → AI → Policies holds the switches:

Control Setting key Effect
External models allowed ai.external.allowed Off means no cloud provider is used at all
Sensitivity ceiling ai.external.sensitivity_ceiling Highest class permitted to leave: normal, confidential, restricted
Default provider ai.provider.default Which configured provider generation routes to

A provider you connect with your own API key is write-only in the admin: after saving, the screen shows only whether a key is set plus a short hint, never the key. There is no reveal and no in-place edit, so rotating a key means deleting the provider entry and connecting again.

The AI audit trail is tamper-evident

Every AI call lands in an append-only _ai_audit row: who acted, which provider and model, capability, token counts, the policy decision, the outcome, and the redacted prompt and output. Each row also stores a SHA-256 of the raw prompt and output — a correlator, so a row ties back to what was really sent without the raw text ever being persisted.

The rows form a hash chain: each commits to the previous row's hash and to its own contents. Settings → AI → Audit has a Verify integrity action that recomputes the whole chain and reports how many rows verified, or marks the chain broken — the response names the first row that fails to recompute. Editing a row, deleting one, or reordering them all break verification. The same screen exports the filtered stream as an .xlsx workbook.

Retention is set with ai.audit.retention_days (empty or 0 keeps everything). When it's on, a nightly job archives before it prunes: the old prefix of the chain is sealed into a JSON bundle under <data dir>/ai_audit_archive, and only then are those rows removed. Each bundle records its own chain seed and anchor, so it stays independently verifiable offline, and the live chain still verifies from where the prune left off. If the archive write fails nothing is deleted — and if the chain is already broken the prune refuses to run, so pruning can never hide tampering.

Note

This is the AI-specific trail. The core's general audit log is a separate append-only hash chain with its own verification — see How Railbase is secured.

The AI acts under its own account

The AI is not a service key borrowed from a person. Each company gets exactly one platform AI principal, stored in _ai_agents and created the first time that agent is used in that company. When the agent executes an action it goes through the ordinary request pipeline under its own identity — the same authentication, role checks and audit a human request gets, resolving to the AI, not to a user. Audit rows carry the actor type ai, so an AI action is never indistinguishable from a person's after the fact. Disabling the agent pauses execution without deleting the principal or its permission grants, so attribution survives.

A module declares whether it may use an external model

A business module declares its AI needs in its bundle manifest, and the platform enforces the declaration rather than trusting it:

  • The declared requires list is a closed set — calling an AI capability outside it is denied.
  • external_allowed defaults to false. When a module has not declared external use, cloud providers are removed from the candidate list for that module's calls — not warned about and allowed. Local backends still work.
  • If such a module runs where the only configured provider is a cloud one, it gets a clear policy denial: add a local provider, or the module must declare the need.

The only route to an external model is the AI engine

A module cannot bypass the redaction and policy layer by making its own HTTP call. The core's outbound-HTTP capability for modules refuses foundation-model API hosts outright — a built-in list covering the major vendors — and re-checks on every redirect hop, so a harmless-looking URL cannot redirect into one. You can extend that list for your own environment with ai.egress.deny_hosts (Settings → Network access), which applies live. The block is one-sided by design: it constrains a module's raw fetch, not the AI engine's own client — the engine is the sanctioned channel precisely because it redacts and applies policy first.

FAQ

Can I use AI with no internet access at all?

Yes, if you run a local model — a local server or the in-process model file. Turning off ai.external.allowed means no cloud provider is used at all.

If I use a cloud provider, what exactly leaves?

The prompt and system text after secret redaction, to the provider you configured with your own key, and only if the payload's sensitivity class clears your ceiling — restricted never does. The audit row stores that same redacted text, so you can read afterwards what left. railbase.app is never in the AI path — it handles the catalog, licences and signed artifacts only.

Can someone quietly delete an AI action from the log?

Not without it showing. Deleting, editing or reordering a row breaks the hash chain, and Verify integrity fails, naming the first row that no longer recomputes.

Was this page helpful?Thanks for your feedback!