Configuration
A Marina server reads process environment values first, then Bun’s .env files in the instance’s
working directory. The marina CLI layers its own settings files instead (next section). Every setting is optional. .env.example is a short
starter to copy; config/environment.reference is the
complete annotated catalog that the dashboard settings editor (Admin → Settings) and the
generated environment reference are built from.
Connect a model
Section titled “Connect a model”Agents need a model: a provider key, or a model running on your machine. Pick the place that matches how you use Marina:
| You use | Do this | It applies to |
|---|---|---|
marina in a folder, or marina start |
marina init: choose a provider and paste its key, or choose local and pick a model your Ollama, llama.cpp, LM Studio or vLLM server already serves |
every folder and every marina start (it writes ~/.marina/.env; inside a source checkout, the checkout’s .env, which marina also reads in every folder) |
| The dashboard | Admin → Keys: paste, save, Test | that server’s world (stored in its database, no restart) |
| The desktop app | Connect an AI provider on first launch, or Admin → Keys | the app’s world |
| A source checkout, Docker or a service | a variable such as ANTHROPIC_API_KEY=… in the environment or the instance’s .env |
that process |
Each folder session runs its own world with its own database, so a key saved in a dashboard’s
Admin → Keys reaches only that world. For keys every folder shares, use marina init or
~/.marina/.env.
Supported keys: ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, OPENROUTER_API_KEY
(one key, models from every vendor), GROQ_API_KEY, TOGETHER_API_KEY, NEBIUS_API_KEY,
HUGGINGFACE_API_KEY and more. A local model
is a base URL: OLLAMA_BASE_URL=http://localhost:11434/v1 (with MARINA_DEFAULT_OLLAMA_MODEL to
pin one), or LLAMA_BASE_URL plus MARINA_DEFAULT_LLAMA_MODEL for llama.cpp and any other
OpenAI-compatible server (one model explains what changes).
Which model the coder uses. In a folder session, Marina’s coding agent uses
MARINA_DEFAULT_MODEL when set, else the first configured provider’s default in the order
Anthropic, OpenAI, Google, OpenRouter, Together, Nebius, Groq, Hugging Face (override one with
MARINA_DEFAULT_<PROVIDER>_MODEL, e.g. MARINA_DEFAULT_ANTHROPIC_MODEL), else a pinned local
model. The welcome card names it. To change it for this launch:
marina --model openrouter/<model-id>Inside the session, /model anthropic/claude-sonnet-5 switches the model and /fast picks the
cheapest model your keys reach. Or run Claude Code, Codex or pi with their own login:
marina --agent claudeResidents, crews and judges choose their models by tier, below in
Choose models. Check what works with readiness providers in
any Marina terminal (/world readiness providers in a folder session); it makes one small call
per provider.
Where the marina CLI reads settings
Section titled “Where the marina CLI reads settings”The marina command works the same in every folder and never reads your project’s own .env
(that belongs to your app). Highest first:
| Layer | File | Use it for |
|---|---|---|
| Shell | export KEY=… |
one-off overrides |
| This folder’s Marina | ~/.marina/projects/<folder>/.env |
settings for one project’s sessions (next to its database) |
| You | ~/.marina/.env |
provider keys and defaults for every folder; marina init writes here |
| Source checkout | <marina checkout>/.env |
a checkout’s existing settings, when running from one |
Each layer fills only what a higher one leaves unset. marina start treats its working directory
as the instance, so a server folder’s .env sits in the second row. marina init writes
~/.marina/.env unless run inside the checkout or given --directory.
bun run init --yes --preset minimal --directory /path/to/instancebun run init --check --directory /path/to/instanceminimal starts the empty world without background agents or auxiliary listeners.
workbench selects the default world. shared-team selects the same default world with sign-in and
persistent auth storage, generating a private auth secret when absent. All presets bind
loopback initially. Configure your public URL, TLS proxy, verified admins and deliberate
bind policy before exposing a shared instance. Capability presets and trust profiles are
separate choices.
Existing settings and comments are preserved; existing values override preset defaults.
--print previews a preset with secrets redacted. --check validates without writing.
Files are written atomically with mode 0600. Run marina start from that instance directory
when using the installed CLI. Process environment overrides remain effective at startup.
Use readiness for actual capability configuration and readiness providers for live
connectivity checks (the latter may spend provider tokens).
What Marina stores, what it sends where, and the switches for each web source (robots.txt, the keyless DuckDuckGo and Yahoo Finance fallbacks) are in Data, privacy and terms.
Minimal Setup
Section titled “Minimal Setup”No configuration needed for local development. Just run:
bun run startThis starts the dashboard and default Workbench on loopback under the local trust profile. The
model API accepts a key Marina generates into <DB_PATH>.local-api-key and prints at boot; the
Memory API (/mem) stays closed until MEM_API_KEYS is set. Agents need one provider key (or a
local model) to think. With AGENT_AUTORESPAWN unset, the seeded Workbench agents start at boot on
a local install that has a provider; set AGENT_AUTORESPAWN=false to keep them stopped, or true
to start them on a shared or public instance. Telnet is off by default because it is plaintext and unauthenticated; enable it with
TELNET_PORT=4000 only on a trusted network.
Common Configurations
Section titled “Common Configurations”Trust profile: local (ungated) vs shared vs public
Section titled “Trust profile: local (ungated) vs shared vs public”Marina decides who it is for from how it is bound, and removes friction accordingly:
| Profile | When (if MARINA_PROFILE is unset) |
What it means |
|---|---|---|
local |
every listener binds loopback and MARINA_AUTH is off |
Ungated for you. Every safety gate except live trading (trading.live) auto-passes for the loopback operator, every loopback person login is sovereign, your arbitrary host commands run without a prompt (code exec-mode auto), rate limits, login caps and memory budgets are off, database durability defaults to normal. An agent’s host shell (shell.exec) and arbitrary host commands (code.exec.unrestricted) are held for your approval. Audit stays on. |
shared |
MARINA_AUTH=better-auth is on |
Gates, ranks and limits enforced; sign-in identifies people. |
public |
any non-loopback bind without sign-in | Everything enforced; passwordless names carry no authority. |
A fresh bun run start on your own machine is therefore local with nothing to configure. To keep
the gates on a personal instance anyway, set MARINA_AUTONOMY=guarded or MARINA_PROFILE=shared.
Forcing MARINA_PROFILE=local on a public bind is a fatal startup error unless you set
MARINA_ALLOW_INSECURE_PUBLIC=true — enabling sign-in does not help, because local ungates every
gate for every signed-in user. The boot log prints the resolved profile and
why. The realistic risk on a local instance is a poisoned shared-pool note leading an agent to run a
host command, so that is the one thing still held: an agent’s run raw / shell and its
non-allowlisted Code Mode commands raise a challenge (challenge approve <token>, or
code exec-approve <token> in Code Mode) and run once you approve them. The agent is never
blocked while it waits. Agents promoted on an older database may still hold shell grants:
gate audit lists them and gate reset agents all apply revokes them.
To lift every gate regardless of profile, for a sovereign, self-hosted Marina with no guests (a
homelab or a private network you control), set MARINA_AUTONOMY=sovereign. Every gate (the core
included), rank floor and hold is off for every principal, agents’ host commands included; audit stays on, and the profile still
sets the performance defaults. On a non-loopback bind it also needs
MARINA_ALLOW_INSECURE_PUBLIC=true, signed in or not. MARINA_AUTONOMY=open is not this switch:
it never lifts the core.
Set yourself as admin
Section titled “Set yourself as admin”MARINA_ADMINS=YourName bun run startWhen YourName logs in from a loopback connection, it is bootstrapped as a sovereign and receives
the rank-tiered operator gates. Remote connections claiming the name are refused, and the list is
redundant under the local profile, where every loopback login is already sovereign; with sign-in
on, use MARINA_AUTH_ADMIN_EMAILS instead. Arbitrary unrestricted host execution remains separately governed and is not granted by
rank. Multiple admins:
MARINA_ADMINS=Alice,Bob bun run startChoose a world
Section titled “Choose a world”MARINA_WORLD=showcase bun run startAvailable worlds (default and empty are core; the others are demo worlds, marked [demo] by
marina worlds — see feature status):
| World | What You Get |
|---|---|
default |
Four-room reactive Workbench with Host/Builder/Critic/Chronicler, Demo Pulse tasks, and progressive complexity. |
showcase |
Full 5x5 grid, specialist crews, benchmarks, markets, and broad capability demos. |
commons |
Pre-seeded projects and room templates on a themed grid. |
research |
Lab, observatory, archive spaces. Good for structured experimentation. |
personal |
Privacy-focused workspaces. Good for a solo agent evolving itself. |
craft |
Workshop + review spaces. Good for spec-driven development. |
evolve |
8 benchmark objectives. Good for testing agent capabilities. |
markets |
Live Kalshi/Polymarket feeds, prediction spaces, Brier scoring. Good for forecasting. |
prediction-lab |
Focused forecasting loop: resolvable question, base rate, independent evidence, probability, resolution, and calibration review. |
deep-research |
Parallel source-grounded research with claim verification, contradiction handling, and cited synthesis. |
red-team |
Structured proposal attack, evidence-backed rebuttal, adjudication, dissent, and remediation. |
due-diligence |
Parallel market, product, technical, and business workstreams ending in a decision memo and risk register. |
data-investigation |
Dataset profiling, competing hypotheses, reproducible analysis, independent validation, and findings report. |
demos |
Lobby, workshop, bridge. Good for interactive demonstrations. |
empty |
One empty space. Good for building everything from scratch. |
Choose models (cheap by default)
Section titled “Choose models (cheap by default)”Work that names no model runs on one of two model tiers
(src/config/model-tiers.ts):
| Tier | Runs | Unset |
|---|---|---|
cheap — MARINA_MODEL_TIER_CHEAP |
residents spawned without a model, room agents, boot-seeded crews (the Workbench, answerer and orchestration crews, the Chronicler), memory helpers, the memory dream pass | MARINA_DEFAULT_MODEL or a runtime default when one is set; else the cheapest adequate tool-capable model for your keys (openrouter/deepseek/deepseek-v4.1-flash-1017 with an OpenRouter key; anthropic/claude-haiku-5-5, openai/gpt-6-luna, … otherwise); else marina/default |
strong — MARINA_MODEL_TIER_STRONG |
what grades or overrules: the benchmark judge, outcome extractor, lesson writer and judge, decision classifier, the Arbiter | marina/default, exactly as before — judging never turns cheap by accident |
One line moves a tier; one entry moves a single role:
MARINA_MODEL_TIER_CHEAP=openrouter/deepseek/deepseek-v4.1-flash-1017 # every resident and crewMARINA_MODEL_TIER_STRONG=openrouter/anthropic/claude-opus-5.5 # every judge and the ArbiterMARINA_AGENT_MODELS=role:crew=anthropic/claude-sonnet-5-5,Host=openai/gpt-6.1-solRoles are resident, room, crew, helper, dreamer (cheap tier), judge,
outcome-judge, lessons-judge, decisions and arbiter. Older specific variables still win
over the tier (MARINA_CREW_MODEL, MARINA_WORKBENCH_MODEL,
MARINA_BENCHMARK_JUDGE_MODEL, MARINA_DECISION_MODEL, …), and so do a
model named on agent spawn, agent config <name> model …, and a per-agent
MARINA_AGENT_MODELS entry. marina/cheap and marina/strong are also model
ids on /v1, resolved per request, so an agent saved on marina/cheap follows
a later tier change after a restart. The /v1 marina model is unchanged.
admin models (readable by everyone) prints the effective model for each tier
and role and where it came from; the boot log prints the two tier lines.
A marina/cheap request that sets no max_tokens gets the cheap tier’s output
cap, MARINA_MODEL_TIER_CHEAP_MAX_OUTPUT_TOKENS (default 16384; on OpenRouter
also a reasoning budget of 3/4 of it), so a reasoning loop cannot run to the
provider’s own limit. A caller’s own max_tokens always wins, larger or
smaller; Marina’s agents and helpers send their own caps and keep them; explicit
model ids and the strong tier are never capped; 0/off turns it off. A reply
cut at the cap is marked x-marina-output-cap: <n>; hit and outputCapHit on
its trace, the benchmark harness counts those as outputCapHits, and admin models shows the cap and how often it hit since start. The same holds inside
marina/verify:<proposer>+<checker>: a marina/cheap or marina/strong member
resolves through the tiers, and a cheap one gets the cap on its calls.
Escalation stays where it was: the Arbiter runs only on contested items, and
model:route / earned routing can pick a stronger candidate per agent.
Upgrading: with no model variable set, residents, room agents, seeded crews
and memory helpers move from marina/default (and the Workbench from each
provider’s flagship) to the cheap tier. Judges, decisions and the Arbiter do
not change. An operator who set MARINA_DEFAULT_MODEL, a runtime default,
MARINA_CREW_MODEL or MARINA_WORKBENCH_MODEL sees no change for what those
cover. Agents saved with an explicit model keep it.
Change ports
Section titled “Change ports”WS_PORT=8080 TELNET_PORT=4001 MCP_PORT=8081 bun run startHost names the server answers to
Section titled “Host names the server answers to”On a loopback bind (the default) and under the local profile, the HTTP and WebSocket listener
answers only to loopback Host names (localhost, 127.0.0.1, [::1]) and the BETTER_AUTH_URL host, and returns 421 for
anything else, so a web page on a DNS name that points at 127.0.0.1 cannot reach your instance.
If you reach it by another name — a reverse proxy on the same host, a LAN name — list it:
MARINA_ALLOWED_HOSTS=marina.example.com,marina.lan:3300 bun run startA bare name matches on any port, name:port only on that port. On a non-loopback bind Host is
not checked unless you set this variable. /mcp on its own port uses MARINA_MCP_ALLOWED_HOSTS.
Secure the model API
Section titled “Secure the model API”MODEL_API_KEYS=sk-my-secret-key-1,sk-my-secret-key-2 bun run startNow API requests need Authorization: Bearer sk-my-secret-key-1 (the same keys guard MCP). Without
this variable the model API accepts only the generated local key under the local profile and is
closed otherwise.
Connect Discord or Telegram
Section titled “Connect Discord or Telegram”For Discord:
DISCORD_TOKEN=your-discord-bot-token bun run startFor Telegram:
TELEGRAM_TOKEN=your-telegram-bot-token bun run startSee Discord & Telegram for bot setup.
All Environment Variables
Section titled “All Environment Variables”The complete list, with defaults and which settings the dashboard may not edit, is the
environment reference, generated from
config/environment.reference. Room agents spawned by a
world authenticate with an internal token generated at startup, so they need no MODEL_API_KEYS
entry, only a configured provider or reachable local model. For collector export, see
Execution Traces and Evaluations.
Flywheel isolated execution (optional)
Section titled “Flywheel isolated execution (optional)”Flywheel is additive: Marina and local Code Mode work normally when these variables are absent. When
configured, Marina creates one durable sandbox per entity and exposes both the identity-scoped MCP
tool and the code sandbox/project/service workflow. Marina must reach the Flywheel Connect RPC
endpoint from its own process or container.
| Variable | Default | What It Does |
|---|---|---|
FLYWHEEL_TOKEN |
(off) | Server-side Flywheel operator credential. Enables the integration; never returned to entities or persisted in Marina. |
FLYWHEEL_RPC_URL |
http://localhost:8088/rpc |
Flywheel Connect RPC base URL as seen by Marina. In Docker, localhost means the Marina container, so use a reachable service or host address. |
FLYWHEEL_IMAGE |
localhost/h2oai/flywheel-agentd:latest |
Default image for code sandbox start and MCP flywheel create. The image must be resolvable by the configured Flywheel backend. |
The live qualification knobs (MARINA_FLYWHEEL_LIVE_*) are in
Release qualification. Start with code doctor, then
code sandbox status. Configuration alone never changes a coding
session from local to Flywheel, and a Flywheel failure never retries a sandbox command on the host.
See Coding, Flywheel integration and OpenShell sandboxes (experimental).
Drop-in Compatibility (Passthru)
Section titled “Drop-in Compatibility (Passthru)”Marina plays three roles with respect to agents — participant (agents inside worlds), consumer (Marina calling out to upstream LLMs), and passthru (external clients calling in). This section is about passthru.
External OpenAI-compatible clients point at Marina by way of compat profiles registered in src/net/adapters/compat-profiles.ts. Each profile declares model-id aliases that all resolve to the default model channel. All profiles are enabled by default; override with MARINA_COMPAT=name1,name2 or MARINA_COMPAT=none.
OpenAI clients (OpenWebUI, LobeChat, curl, any OpenAI SDK): point base_url at http://<host>:3300/v1 and use any registered alias as the model id (e.g. assistant) or just marina. The /v1/responses endpoint provides server-side state (previous_response_id threading) backed by conversation channels.
Ollama clients: same host, use /api/tags, /api/chat, /api/generate.
Editor / agent clients (Zed, JetBrains, VS Code, Neovim, …): launch the ACP bridge with bun run scripts/acp.ts <name> — stdio ndjson JSON-RPC 2.0 speaking ACP protocol 1. ACP is a generic protocol; any client that speaks it works.
MCP clients: /mcp endpoint on :3301.
Adding an alias is a one-line entry in src/net/adapters/compat-profiles.ts. Today’s registered profiles:
| Profile | Aliases |
|---|---|
openai |
assistant |
Compat profiles only register model-id aliases on /v1/models and resolve to the default model channel — they are independent of which world is loaded. Disable them with MARINA_COMPAT=none.
Tabular Foundation Model (TabH2O)
Section titled “Tabular Foundation Model (TabH2O)”Marina is built by H2O. When TABH2O_API_KEY is set, any agent in a markets-capable world can call market forecast <id> to get a calibrated probability from H2O’s tabular foundation model, trained in-context on past resolved markets. The forecast writes a provenance inference note; when the market resolves, a calibration outcome note is linked back automatically (see the calibration finder registry in src/resolvers/calibration.ts).
| Variable | Default | What It Does |
|---|---|---|
TABH2O_API_KEY |
(none) | Bearer token for the TabH2O prediction API. Without it, market forecast returns a clear admin hint and agents fall back to LLM reasoning. |
TABH2O_ENDPOINT |
https://tabh2o.h2oai.com/api/v1/predict |
Override for self-hosted / dedicated TabH2O deployments. |
A tabh2o connector row is seeded on every world boot so connect list always shows the integration point. Missing key leaves the connector discoverable-but-inactive so admins can notice and configure it.
Production Example
Section titled “Production Example”WS_PORT=8080TELNET_PORT=0 # telnet off (plaintext/unauthenticated) — recommended in productionMCP_PORT=8081LOG_PORT=8082DB_PATH=/data/marina.dbASSETS_DIR=/data/assetsMARINA_WORLD=defaultMARINA_ADMINS=Alice,BobMODEL_API_KEYS=sk-prod-key-1,sk-prod-key-2LOG_FORMAT=jsonLOG_LEVEL=infoDISCORD_TOKEN=your-discord-bot-tokenTELEGRAM_TOKEN=123:ABC...Docker
Section titled “Docker”docker build -t marina .docker run -d --name marina \ -p 127.0.0.1:3300:3300 \ -v marina-data:/app/data \ --env-file .env \ -e MARINA_PUBLISHED_HOST=127.0.0.1 \ marinaThe image binds 0.0.0.0 inside the container, so boot needs MARINA_PUBLISHED_HOST to know the
host publish address; without it the wildcard bind counts as network exposure and boot stops
with a FATAL error while passwordless login is in effect. Keep the 127.0.0.1: publish prefix and MARINA_PUBLISHED_HOST in step (add
-p 127.0.0.1:3301:3301 for MCP the same way). Docker Compose, the local overlay and wider exposure
are covered in Deployment.
Hard-Coded Limits
Section titled “Hard-Coded Limits”These aren’t configurable via env vars but are good to know (the per-IP WebSocket connection cap
is: WS_MAX_CONNECTIONS_PER_IP, default 100):
| What | Value |
|---|---|
| Max total WebSocket connections | 1000 |
| WebSocket idle timeout | 255 seconds |
| Max commands processed per tick | 1000 |
| Command queue size before dropping | 5000 |
| Dashboard update interval | 2 seconds |
