Run an AI agent on a machine you control
Nocturn is a self-hosted personal AI agent with a built-in CPU miner. One gateway runs on your computer or server, you drive it from the terminal, the desktop app, or your chat apps, and it asks before doing anything risky. These pages cover all of it, from the first install to the HTTP API.
What this website is
nocturnh.com is the product page and documentation for Nocturn. It is a static site. No agent runs here, no account exists here, and nothing you type on this site is sent to a server. The home page shows what Nocturn does, with a scripted demo that calls no model and no tool, and this page explains how to use it.
The page you are reading, served from Cloudflare. The Connect wallet button only asks your browser wallet to switch to Robinhood Chain. Nothing is signed and nothing is sent.
The same page, served by your own gateway at 127.0.0.1:18790 with a private token. There it also edits the Trusted by list and links a wallet to your miner.
Nocturn itself is an npm package: a gateway, a CLI, chat channel adapters, a scheduler, a miner, and a pool server. It lives on your machine, never on this site.
So, if you came here wondering what the site does: it tells you about Nocturn and gives you the install command. The real work happens after you run that command on your own computer or server.
The link printed by nocturn dashboard contains your token. Treat it like a password and never paste it anywhere public.
Overview and architecture
Everything is one Node process called the gateway, plus a thin CLI that talks to it over HTTP. There is no database and no cloud account. State is JSON files in one folder.
The parts
An HTTP server on 127.0.0.1:18790. Serves the dashboard, exposes the API, streams events, and owns everything below.
The loop that sends your conversation to the model, runs the tools it asks for, and pauses for approval when a tool needs it.
nocturn. Starts and stops the gateway, chats, manages channels, jobs, memory, skills, and config by calling the API.
Telegram, Discord, and Slack adapters. Each inbound chat becomes a session; replies and approval questions go back to the same chat.
Cron jobs that run a prompt on a schedule and can deliver the reply to a chat.
Files, shell, web fetch, headless browser, memory, skills, schedules, and outbound messages, each with its own permission policy.
nocturn mine. Hashes SHA-256 on worker threads, auto-tunes thread count by temperature, and reports shares to a pool.
nocturn pool run. Hands out jobs each round, verifies shares, and splits the round reward by work done.
How a message flows
- You send textFrom
nocturn chat, the dashboard, a Telegram message, or a cron job. It lands onPOST /api/sessions/:id/messages. - The agent calls the modelAnthropic by default, with adaptive thinking, or any OpenAI-compatible endpoint such as Ollama. Text streams out as events while it is generated.
- The model asks for toolsEach tool call is validated, then checked against its policy.
autoruns now,askwaits for you,denyis refused. - You approve or denyIn the terminal, with
nocturn approvals, in the dashboard, or by replying/approvein the chat that started the run. A question times out after 10 minutes. - Results go back to the modelUp to 40 steps per turn. When the model stops asking for tools, the final text is the reply, and the whole conversation is saved to disk.
The data folder
Everything lives in ~/.nocturn (or wherever NOCTURN_HOME points). Keys are stored in plain text. Never commit or share this folder.
~/.nocturn/
├── config.json provider, model, keys, persona, policies, channels
├── token the bearer token for the API and the dashboard link
├── memory.json saved memories, one entry per line of the system prompt
├── jobs.json cron jobs with their last run and status
├── partners.json the Trusted by list shown on the home page
├── wallet.json the signed wallet proof, if you linked one
├── miner.json miner name, pool address, pool token
├── pool.json accounts and balances, only if you host a pool
├── gateway.pid pid of the background gateway
├── gateway.log its log, read with nocturn gateway logs
├── browser/ the headless browser profile
├── sessions/ one JSON file per conversation
└── workspace/ the only folder the file tools can touch
└── skills/ one folder per skill, each with SKILL.md
Install
Nocturn needs Node 22 or newer and a model: a Claude API key, or any OpenAI-compatible endpoint. Install it once from npm, then run the guided setup.
Linux and macOS
$ npm install -g @morpheapp/nocturn
$ nocturn onboard
Windows
PowerShell blocks the .ps1 shims that npm installs, so call the .cmd versions.
$ npm.cmd install -g @morpheapp/nocturn
$ nocturn.cmd onboard
If you would rather use the short names, allow local scripts once for your account and open a new terminal:
$ Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
What onboard does
- Picks a provider
anthropicor an OpenAI-compatible endpoint. For Anthropic it asks for the key and a model name; for OpenAI it asks for the base URL, model name, and an optional key. - Optionally connects TelegramPaste a bot token from BotFather. You can add Discord and Slack later with
nocturn channels add. - Starts the gatewayIn the background, and offers to install it as a login service so it survives reboots.
Check that everything is healthy with nocturn doctor, then talk to it with nocturn chat.
Desktop and mobile apps
You do not have to live in the terminal. The Download section of the home page has installers for each platform.
An Electron app in the desktop/ folder of the repository. It starts the gateway when it launches and opens the dashboard in its own window, so one double-click gives you the whole product. Windows gets an .exe installer, macOS a .dmg.
Built as an .apk from the mobile/ folder through GitHub Actions. It is a remote client: you enter the address and token of a gateway you run elsewhere, reached through an SSH tunnel, and the dashboard opens in the app.
Open /panel.html on your own gateway and add it to the home screen of any phone. Like the Android app, it talks to a gateway on a computer or server.
The gateway itself needs Node and runs on a computer or a server, never on the phone. The mobile apps control a gateway; they do not contain one.
Command line
Every command calls the gateway's API with the token from ~/.nocturn/token. If the gateway is not running, most commands tell you so. Add --json to any listing command for machine-readable output, and --version prints the version.
Setup
| Command | What it does |
|---|---|
nocturn onboard | Guided setup: provider, key, model, optional Telegram, gateway start, optional login service. nocturn setup is an alias. |
nocturn doctor | Checks Node version, dependencies, the state folder, the gateway, the model key, and each enabled channel. Exits 1 if something fails. |
nocturn config get [key] | Prints the config with secrets redacted. Dotted keys work: nocturn config get channels.telegram. |
nocturn config set <key> <value> | Sets one key. Values that parse as JSON are stored typed, so true is a boolean. Channel changes need nocturn gateway restart. |
nocturn dashboard | Prints the private dashboard link with the token, and the SSH tunnel command for servers. |
Gateway
| Command | What it does |
|---|---|
nocturn gateway run | Runs in the foreground. Ctrl+C stops it. |
nocturn gateway start | Starts a detached background process and writes gateway.pid. Logs go to gateway.log. |
nocturn gateway stop / restart | Stops the background process by pid, or stops and starts it. |
nocturn gateway status | Same as nocturn status. |
nocturn gateway logs [--lines N] [-f] | Prints the last 50 lines, or N. -f or --follow keeps printing new lines. |
nocturn gateway install / uninstall | Registers a login task on Windows (schtasks) or a user service on Linux (systemd --user). |
nocturn status | Gateway, model readiness, channel status, session and job counts, pending approvals and pairing requests. |
Talking to the agent
| Command | What it does |
|---|---|
nocturn chat [--session id] | Interactive chat in the terminal. Starts the gateway if needed. /new starts a fresh conversation, /sessions lists them, /status shows status, /exit leaves, Ctrl+C stops a running task. Tool calls are shown as they happen and approvals are asked inline. |
nocturn agent -m "text" [--session id] [--json] | One task, streamed reply, then exit. With --json prints { sessionId, reply }. Without a terminal, approvals are printed with the id to approve from elsewhere. |
nocturn sessions | Lists conversations with origin, title, and last update. |
nocturn approvals list | Shows tool calls waiting for you. |
nocturn approvals approve <id> / deny <id> | Answers one of them. |
nocturn message send --to <target> -m "text" | Sends a message through a connected channel. Targets look like telegram:123, discord:<channel>, slack:<channel>. |
Channels and pairing
| Command | What it does |
|---|---|
nocturn channels list | Status of each channel and how many senders are allowed. |
nocturn channels add telegram [--token t] | Saves the bot token and turns the channel on. Without the flag it asks, with input hidden. |
nocturn channels add discord [--token t] | Same for a Discord bot token. |
nocturn channels add slack [--bot-token xoxb] [--app-token xapp] | Slack needs both tokens: a bot token and an app-level token with Socket Mode. |
nocturn channels remove <name> | Turns the channel off. The token stays saved. |
nocturn pairing list | Senders waiting to be let in, with their 6-digit codes. |
nocturn pairing approve <code> / deny <code> | Adds the sender to that channel's allow list, or drops the request. |
Automation and knowledge
| Command | What it does |
|---|---|
nocturn cron list | Jobs with schedule, on/off, last run and status. |
nocturn cron add --name n --cron "0 8 * * 1-5" -m "prompt" [--deliver telegram:123] | Adds a job. The cron expression is validated before saving. |
nocturn cron run|enable|disable|rm <id> | Runs one now, toggles it, or deletes it. |
nocturn memory list|add <text>|rm <id> | The facts injected into every system prompt. |
nocturn skills list|rm <name> | Installed skills. Add one by creating a folder, see Skills. |
Mining and website
| Command | What it does |
|---|---|
nocturn mine [--threads N] [--pool URL] | Mines in this terminal until Ctrl+C. Needs a terminal because a person answers the check. |
nocturn mine balance | Prints your NOCT balance from the pool. |
nocturn pool run [--port 18800] [--host 0.0.0.0] [--file path] [--trust-proxy] | Runs a pool server. Put it behind HTTPS before sharing the address. |
nocturn site build [--out folder] | Exports the landing page with your Trusted by list for a static host. |
nocturn help | The full list, in the terminal. |
Environment
NOCTURN_PORT changes the gateway port (default 18790). NOCTURN_HOME moves the state folder. Both must be set for the CLI and the gateway alike, because the CLI finds the gateway through them.
Chat channels
Connect a bot and Nocturn answers from your phone. Each chat becomes its own session, named telegram:<chat>, discord:<channel>, or slack:<channel>, with its own history.
Telegram
- Create a bot with @BotFather and copy its token.
- Run
nocturn channels add telegramand paste it. - Send your bot any message. It replies with a pairing code.
- Run
nocturn pairing approve <code>. From now on that account can talk to Nocturn.
The adapter long-polls getUpdates and only accepts private chats. Replies are split at 4,000 characters.
Discord
- Create an application in the Discord developer portal, add a bot, and enable the Message Content intent.
- Invite it to your server with permission to read and send messages.
- Run
nocturn channels add discordwith the bot token. - DM the bot, or mention it in a channel. Approve the pairing code.
The adapter connects to the Discord gateway over WebSocket with the guild messages, direct messages, and message content intents. In servers it only reacts when mentioned. Replies are split at 1,900 characters and never ping anyone.
Slack
- Create a Slack app with Socket Mode on. Add the
app_mentions:read,chat:write,im:history, andim:readscopes, and subscribe toapp_mentionandmessage.imevents. - Install it to the workspace. Copy the bot token (
xoxb-) and an app-level token (xapp-) withconnections:write. - Run
nocturn channels add slackand paste both. - DM the app or mention it in a channel. Approve the pairing code.
Pairing
Unknown senders never reach the agent. The first message from a new sender gets a 6-digit code and a note saying this Nocturn is private. The request shows up in nocturn pairing list, nocturn status, and the dashboard. At most 20 requests wait at once; after that new strangers are ignored until you clear some. Approving writes the sender id to channels.<name>.allow in the config.
Approvals from chat
When a run that started in a chat needs permission, the question is sent to that same chat, with the first 300 characters of the tool input:
Allow shell?
{"command":"systemctl restart app"}
Reply /approve or /deny
Reply /approve or /deny. If nothing is waiting, the bot says so. The dashboard and nocturn approvals can answer the same question.
Tools and permissions
The model gets twelve tools. Each has a policy in config.policies: auto runs without asking, ask waits for your answer, deny refuses and tells the model the tool is disabled.
| Tool | Default | What it does |
|---|---|---|
read_file | auto | Reads a UTF-8 file from the workspace. Output is clipped at 50,000 characters. |
list_files | auto | Lists a workspace directory. Folders end with /. |
memory_search | auto | Keyword search over saved memories. An empty query lists everything. |
memory_save | auto | Saves a durable fact about you or your work. |
load_skill | auto | Returns the full SKILL.md of an installed skill. |
write_file | ask | Creates or overwrites a file in the workspace, creating folders as needed. |
edit_file | ask | Replaces one exact occurrence of text. Fails if the text occurs zero or several times. |
shell | ask | Runs a command in the workspace: PowerShell on Windows, sh elsewhere. Two-minute timeout. |
web_fetch | ask | Fetches a public http(s) URL and returns its text, with HTML tags stripped. Follows up to 5 redirects, 30-second timeout. |
browser | ask | Drives a headless Chrome or Edge: navigate, text, click, type by CSS selector, and screenshot into the workspace. |
schedule_task | ask | Creates a cron job. |
send_message | ask | Sends text through a connected channel. |
Change a policy with nocturn config set policies.shell auto, or in the dashboard. Valid values are auto, ask, and deny.
The approval flow
- Validation firstTool inputs stream in eagerly and are not validated by the API, so Nocturn checks required fields, types, and enums itself before anything runs. A bad input is reported to the model without running.
- Then the policyAn
asktool emits anapprovalevent with the tool name and full input. The run pauses. - You answer anywhereTerminal prompt,
nocturn approvals approve <id>, the dashboard, or/approvein the chat. A denied or expired request (10 minutes) tells the model the owner did not approve, and the model is instructed not to work around it.
Safety model
- The gateway listens on
127.0.0.1only, requires the bearer token for every API call, and rejects requests whoseHostorOriginis not local. That blocks DNS rebinding and cross-site requests. - File tools resolve symlinks and refuse any path that leaves
~/.nocturn/workspace. - Web and browser tools resolve the hostname first and refuse private, loopback, link-local, and multicast addresses, including IPv4-mapped IPv6. The agent cannot reach your gateway, your LAN, or cloud metadata endpoints.
- The system prompt tells the model that web pages, files, tool output, and relayed chat messages are data, not instructions from you.
- Secrets are never returned by the API. The config endpoint redacts them, and a blank value in an update keeps the stored secret.
The shell tool is not sandboxed. An approved command runs with your user's full permissions, in the workspace folder but able to reach anything your user can. Read each command before allowing it, and do not set shell to auto on a machine with data you care about.
Skills, memory, and schedules
Skills
A skill is a folder in ~/.nocturn/workspace/skills/ with a SKILL.md inside. The name must be lowercase letters, digits, and dashes. Its description is listed in every system prompt; the body is loaded only when the model calls load_skill, so you can keep long instructions without paying for them on every turn.
---
name: weekly-update
description: How to write the Monday update for the team
---
Read notes/week.md, group the items by project, and write
briefs/update.md with three sections: shipped, in progress, blocked.
Keep each line under 15 words.
Create skills by hand, with POST /api/skills, or in the dashboard. Remove one with nocturn skills rm weekly-update.
Memory
Memories are short facts, one per entry, stored in memory.json and injected into every system prompt under “Saved memories”. The model can save them with memory_save and search them with memory_search; you can manage them with nocturn memory.
$ nocturn memory add "I prefer short answers"
$ nocturn memory list
$ nocturn memory rm <id>
Cron jobs
A job runs a prompt on a 5-field cron schedule, in the server's local time. Each run is its own session named cron:<job id>, so a job keeps its history across runs. If the job has a deliver target, the reply is sent there.
$ nocturn cron add --name brief --cron "0 8 * * 1-5" -m "plan my day" --deliver telegram:42
| Field | Range | Examples |
|---|---|---|
| minute | 0 to 59 | 0, */15, 0,30 |
| hour | 0 to 23 | 8, 9-17, */2 |
| day of month | 1 to 31 | *, 1, 1-7 |
| month | 1 to 12 | *, 6-8 |
| weekday | 0 (Sunday) to 6 | 1-5, 0,6 |
Lists, ranges, and steps are supported; names like MON are not. The scheduler checks once a minute and only fires enabled jobs. nocturn cron run <id> fires one immediately, which is the easiest way to test a prompt.
HTTP API
The CLI and the dashboard use this API, and so can your own scripts. Base URL http://127.0.0.1:18790/api. Every call needs Authorization: Bearer <token> with the token from ~/.nocturn/token. Bodies and responses are JSON. Bodies are limited to 1 MB.
$ TOKEN=$(cat ~/.nocturn/token)
$ curl -s http://127.0.0.1:18790/api/state -H "Authorization: Bearer $TOKEN"
Requests are refused with 403 unless the Host header is 127.0.0.1, localhost, or [::1], and the Origin header is either absent or matches. A wrong token gets 401. Errors come back as { "error": "message" }.
State and events
| Route | Body | Returns |
|---|---|---|
GET /api/state | — | Redacted config, tool names, sessions, pending approvals, running sessions, channel status, pairing requests, jobs, skills, memory, and which credentials are present. |
GET /api/events | — | A Server-Sent Events stream. Every event is a JSON object with a type. A comment ping is sent every 25 seconds. |
PUT /api/config | Any subset of the config | New state. Unknown keys are ignored, types must match, blank secrets keep the old value. Changing channels restarts the adapters. |
Sessions and approvals
| Route | Body | Returns |
|---|---|---|
POST /api/sessions | — | 201 with the new session { id, origin, title, messages, updated }. |
GET /api/sessions/:id | — | The full session including message history, or 404. |
DELETE /api/sessions/:id | — | Stops a running turn and deletes the file. |
POST /api/sessions/:id/messages | { "text": "..." } | 202 at once. The run happens in the background; watch /api/events. A session that does not exist yet is created with this id, which is how chat sessions start. |
POST /api/sessions/:id/stop | — | Aborts the running turn. |
POST /api/approvals/:id | { "approved": true } | Resolves a pending approval, or 404. |
Channels, jobs, skills, memory
| Route | Body | Returns |
|---|---|---|
POST /api/pairing/:code | { "approve": true } | Allows or drops the sender. New state. |
POST /api/allow | { "channel", "sender" } | Removes a sender from the allow list. New state. |
POST /api/message | { "target", "text" } | Sends through a connected channel. 400 if the channel is not connected. |
POST /api/jobs | { "name", "cron", "prompt", "deliver" } | 201 with the new state. 400 on a bad cron. |
POST /api/jobs/:id/run | — | 202. Fires the job now. |
PATCH /api/jobs/:id | { "enabled": false } | Toggles the job. |
DELETE /api/jobs/:id | — | Removes it. |
POST /api/skills | { "name", "description", "instructions" } | Writes skills/<name>/SKILL.md. 201 with the new state. |
DELETE /api/skills/:name | — | Deletes the folder. |
POST /api/memory | { "text" } | 201 with the new state. Up to 5,000 characters. |
DELETE /api/memory/:id | — | Forgets it. |
Wallet and partners
| Route | Body | Returns |
|---|---|---|
GET /api/wallet | — | { address } if a wallet is linked, else {}. |
POST /api/wallet | { "address", "message", "signature" } | Verifies the personal_sign proof and stores it. 400 if the signature does not match. |
DELETE /api/wallet | — | Unlinks. |
GET /api/partners | — | The Trusted by list. |
POST /api/partners | { "name", "logo" } | Logo is a data URL: PNG, JPEG, or WebP, under 256 KB, checked by signature. Up to 40 entries. |
DELETE /api/partners/:id | — | Removes one. |
Event types
Everything on /api/events carries a sessionId where it applies.
| Type | Fields | Meaning |
|---|---|---|
turn_start / step_start | — | A turn began; each model call within it began. |
text | text | A streamed piece of the reply. |
thinking | text | A streamed piece of the model's summarized thinking (Anthropic only). |
tool_start | id, name, input | The model asked for a tool. |
approval | id, tool, input | Waiting for you. Answer with POST /api/approvals/:id. |
approval_resolved | id, approved | Someone answered, or it timed out. |
tool_result | id, name, content, isError | What the tool returned. |
turn_end | — | The reply is complete. |
turn_error | message | The turn failed or was stopped. |
sessions_changed, channels_changed, jobs_changed | — | Refresh the matching part of /api/state. |
Mining
nocturn mine hashes SHA-256 on your CPU and reports shares to a pool. Balances are in NOCT, a number kept by the pool server. NOCT is not a coin on a blockchain, cannot be withdrawn, and has no guaranteed value. Only mine on a machine you own or have permission to use.
Start mining
$ nocturn mine
$ nocturn mine --threads 4
$ nocturn mine balance
- Pick a name3 to 20 characters: letters, digits, dash, underscore. The pool returns a token that is saved in
miner.json. The default pool comes from the package, or pass--pool https://..., or setNOCTURN_POOL. - Answer the checkA small addition, asked once when mining starts. Three wrong answers end the session.
- HashBy default all cores but one. The screen shows hashrate, job and round progress, estimated share of the round reward, last payout, balance, hardware, temperature, and what auto-tune is doing.
Press Ctrl+C to stop. Mining only runs while that terminal is open; on a server use tmux so it survives disconnects. The check lasts as long as the miner keeps talking to the pool. If contact is lost for over 30 minutes, mining pauses and the check is asked again.
Auto-tune
Every 15 seconds the miner reads CPU temperature (Linux hwmon, or Windows ACPI thermal zones), GPU temperature through nvidia-smi when available, and free memory. At 85°C or above it idles one thread. Under 8% free memory it idles one thread. Once it is 75°C or cooler for a minute, it brings one back, up to the number you asked for. Virtual servers often expose no sensors, and then it simply keeps your thread count.
Hosting a pool
$ nocturn pool run --host 0.0.0.0 --port 18800 --trust-proxy
Put it behind an HTTPS reverse proxy, then set nocturn.pool in package.json to its address. --trust-proxy reads X-Forwarded-For so the limit of 5 sign-ups per address per hour works behind the proxy. The pool keeps accounts in pool.json, or the file you pass with --file.
Each round lasts 60 seconds and pays 100.00 NOCT, split by work: a share at difficulty d counts as 2d. Difficulty is per miner, starting at 20 bits, raised by one when shares arrive faster than every 2.5 seconds, lowered by one after 20 seconds without a share, between 8 and 44. Duplicate and stale shares are rejected, and a miner is limited to 20 shares per second.
| Pool route | Purpose |
|---|---|
POST /register | { name } → { name, token }. The only route without a token. |
POST /check, /check/answer | Gets a question, answers it. Errors: wrong_answer. |
GET /job | Seed, difficulty, round timing, your work, balance, wallet. Error check_required until the check is passed. |
POST /share | { seed, nonce } → { accepted, reason } plus a fresh job. Error slow_down over the rate limit. |
GET /balance | { cents, wallet }. |
POST /wallet | Stores a verified wallet proof on the account. |
Linking a wallet
On your own dashboard, Connect wallet asks MetaMask (or any wallet that injects window.ethereum) to switch to Robinhood Chain (chain id 4663), then to sign this message with personal_sign:
Nocturn wallet link
Wallet: 0xYourAddress
Issued: 2026-11-03T12:00:00.000Z
The gateway recovers the signer from the signature and refuses anything that does not match the address. No private key is ever sent. The proof is stored in wallet.json and sent to the pool the next time you run nocturn mine, so the pool can show the wallet on your account. On the public site the button only switches networks and signs nothing.
Configuration and servers
config.json
Read with nocturn config get, change with nocturn config set, the dashboard, or PUT /api/config. Unknown keys are dropped and types must match the defaults.
| Key | Default | Meaning |
|---|---|---|
provider | anthropic | anthropic or openai (any OpenAI-compatible endpoint). |
model | claude-opus-5-5 | Anthropic model id. Nocturn enables server-side fallback for the Claude 5 family. |
effort | high | Anthropic effort level. |
anthropicKey | empty | API key. ANTHROPIC_API_KEY in the environment also works. |
openaiBaseUrl | http://localhost:11434/v1 | Ollama by default. Any /chat/completions endpoint with streaming and tools. |
openaiModel, openaiKey | empty | Model name and optional key for that endpoint. |
persona | “You are Nocturn, …” | The first paragraph of the system prompt. |
policies.<tool> | see Tools | auto, ask, or deny per tool. |
channels.<name> | off | enabled, tokens, and the allow list of sender ids. |
Environment variables
| Variable | Effect |
|---|---|
NOCTURN_PORT | Gateway port, default 18790. The CLI reads it too. |
NOCTURN_HOME | State folder, default ~/.nocturn. |
NOCTURN_BROWSER | Path to a Chrome or Edge executable for the browser tool, if it is not in a standard location. |
NOCTURN_POOL | Pool address for nocturn mine, overridden by --pool. |
NOCTURN_TELEGRAM_API | Alternate Telegram API base, for a local Bot API server. |
ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL | Picked up by the Anthropic SDK when the config has no key. |
NO_COLOR | Turns off terminal colors. |
On a server
$ nocturn onboard
$ nocturn gateway install
$ loginctl enable-linger $USER
On Linux this writes a systemd --user unit named nocturn-gateway with Restart=on-failure, and enable-linger keeps user services running after you log out. On Windows it creates a scheduled task that starts at login.
The gateway still listens on 127.0.0.1 only. To open the dashboard from your own computer, tunnel to it, then open the link from nocturn dashboard:
$ ssh -L 18790:127.0.0.1:18790 user@your-server
The same tunnel is what the mobile apps and the installable web app use: they talk to 127.0.0.1:18790 on the phone, which the tunnel forwards to the server.
Website deployment
The landing page in public/ is both the dashboard your gateway serves and the public site. nocturn site build copies it into a folder a static host can serve, writes partners.json with your Trusted by list, and adds a _headers file with the same Content-Security-Policy the gateway sends.
$ nocturn site build --out site
$ npx wrangler deploy
On Windows PowerShell use npx.cmd wrangler deploy, since PowerShell blocks npx.ps1 by default. wrangler.jsonc serves site/ as a Cloudflare Worker with static assets on nocturnh.com and www.nocturnh.com. Any other static host works: upload the folder and keep the headers.
The public site has no gateway, so the Connect wallet button only connects, the Trusted by section comes from the file written at build time, and the numbers in the “Remembers how you like things” tile stay at zero. The token contract and the official X account are published from the admin page at /admin, which needs the admin password set on the Worker.
FAQ and troubleshooting
Start with nocturn doctor. It checks Node, dependencies, the state folder, the gateway, the model key, and each enabled channel, and prints the fix for anything that fails.
“The gateway is not running.”
Run nocturn gateway start. If that fails, read nocturn gateway logs. A common cause is a missing model key: run nocturn onboard.
“Port 18790 is already in use.”
Another gateway, or another program, is on that port. Check with nocturn gateway status. To run on a different port, set NOCTURN_PORT for both the gateway and the CLI.
“npx : File npx.ps1 cannot be loaded because running scripts is disabled.”
That is the PowerShell execution policy, not Nocturn. Use npx.cmd, npm.cmd, and nocturn.cmd, or run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned once.
“No model is set up yet.”
Run nocturn onboard, or set ANTHROPIC_API_KEY in the environment of the gateway process. For the OpenAI-compatible provider, openaiModel must be set.
“No Chrome or Edge found.”
The browser tool looks in the usual install locations. Point NOCTURN_BROWSER at the executable, or install Chrome. The profile is kept in ~/.nocturn/browser.
My bot does not answer on Telegram.
Check nocturn channels list: the status should be connected. Then check nocturn pairing list, because new senders are blocked until you approve their code. Only private chats are accepted on Telegram.
A tool was denied and the model gave up.
That is by design. The approval question waits 10 minutes; if nobody answers, it counts as denied and the model is told. Answer faster, or set that tool to auto if you trust it.
“Stopped after 40 steps without finishing.”
One turn may make at most 40 model calls. Break the task into smaller prompts, or add a skill with clearer instructions.
“The pool does not know this account.”
The pool was reset or you point at a different pool than the one that issued the token. Delete ~/.nocturn/miner.json and run nocturn mine to sign up again.
“nocturn mine needs a terminal.”
A person has to answer the check, so the miner refuses to run without an interactive terminal. On a server, run it inside tmux and detach.
Can I use a local model?
Yes. Pick the OpenAI-compatible provider in nocturn onboard, keep the Ollama base URL, and enter the model name. Prompts never leave the machine. The model needs tool-calling support to use tools.
Where are my keys?
In ~/.nocturn/config.json, in plain text, with file mode 600 where the OS supports it. The API never returns them.
How do I run the tests?
npm test in the repository. The tests use a fake model server, so they need no key and spend nothing.
© Nocturn · nocturnh.com · Needs Node 22 or newer · Your data stays in ~/.nocturn