kernctl

kernctl manages a self-hosted Kern server from a terminal. It is POSIX sh and curl, nothing else required, and it covers exactly what the Kern Platform desktop app covers — a headless server is not a lesser citizen.

What it is

kernctl [--url URL] [--token TOKEN] [--json] [--yes] <command>

It talks to the management API, /kern/v1/admin/*. That surface can read everything about the instance and none of anybody's work: it counts records, measures spaces and lists who belongs to what, but never returns the contents of a task, note or whiteboard.

The installer puts it at /usr/local/bin/kernctl. kernctl help prints the same list as this page.

Global flagWhat it does
--url URLWhich server to talk to, overriding everything else.
--token TOKENWhich token to use, overriding everything else.
--jsonRaw JSON instead of a table. Needs no python3.
--yes, -yDon't ask for confirmation.
--quietLess chatter.

Where it gets its token

On the server's own machine, nothing needs setting up: the server writes a management token on first start and kernctl reads it. Sources are consulted in this order, first match winning:

--url / --token              on the command line
KERN_URL / KERN_ADMIN_TOKEN  in the environment
~/.config/kernctl/config     written by `kernctl use`
/etc/kernctl/config          system-wide, written by the installer
the token file               ADMIN_TOKEN_FILE, or /var/lib/kern/admin-token
http://127.0.0.1:8787        the default

That ordering is why a root shell on the server just works, while a laptop needs to be told once — and why a CI job can set two environment variables and nothing else.

From your own machine

The management API is loopback-only by default, so reach it through an SSH tunnel and remember the choice:

ssh -L 8787:127.0.0.1:8787 you@server
kernctl use http://127.0.0.1:8787 --token kern_…

kernctl use writes ~/.config/kernctl/config with mode 600. Get a token for it with kernctl token new on the server.

If a command is refused with admin_local_only, the address gate stopped you, not your token — the tunnel isn't up, or you are talking to the public address rather than the forwarded one.

Looking at it

CommandWhat it shows
statusWhat this instance is, and how much is in it.
health [--fix] [--fix=ID]Every check, grouped; repairs what can be repaired.
healRepair everything, then check again.
doctorThis machine, and the path from here to the server.
logs [-f] [--level warn]Recent server log. In memory, so empty after a restart.
audit [N]What management tokens have done.
query "SELECT …"A read-only look at the database.

status first, when you don't know what is wrong; doctor when you can't reach the server at all, because it tests the path rather than the server.

kernctl status
kernctl health
kernctl health --fix            # repair everything safe
kernctl health --fix=sessions   # repair one check
kernctl logs -f --level warn

Health repairs are safe by construction: each deletes something that has already expired or lost its parent. Nothing repairable touches live data. See keeping it healthy for what is checked.

People

CommandWhat it does
users list [-q TEXT] [--held] [--unverified]Find accounts. --unverified is who got stuck signing up.
users show <email|id>One account: spaces, roles, sessions.
users hold <email> [reason]Suspend: no new sessions, data untouched.
users release <email>Undo a hold.
users verify <email>Mark the address verified. The way in when there is no SMTP.
users revoke-sessions <email>Sign them out everywhere.
users delete <email> [--with-workspaces]Remove the account, and optionally the workspaces it owns.
allow list · allow add <email> [note] · allow remove <email>Who may sign up when REGISTRATION=invite.
kernctl users list --unverified
kernctl users verify alice@example.com
kernctl users hold bob@example.com "left the company"
kernctl allow add carol@example.com "contractor"

Hold rather than delete when somebody leaves: a hold stops new sessions and leaves everything they contributed to shared workspaces where it is. Deleting is for when they ask you to.

Destructive commands name what they will destroy — deleting an account takes the email as confirmation — so a mistyped id can't take out the wrong one.

Spaces, invites, streams

CommandWhat it does
spaces list [-q TEXT] [--personal|--workspaces] [--deleted]Personal spaces and workspaces, with sizes.
spaces show <id>One space: members, record counts, storage.
spaces delete <id> [--purge]Delete a space; --purge removes it immediately rather than after the grace period.
invites list · invites revoke <id>Outstanding invitations.
streams list · streams enable <id> · streams disable <id>Stream endpoints across the instance.

streams disable is the blunt instrument for a stream being abused from outside: it stops accepting submissions without deleting it or touching its settings, so the owner can fix the rules and you can turn it back on.

Deleted spaces sit in a grace period, which is why spaces list --deleted exists and why kernctl health reports spaces deleted more than 30 days ago as something to clean up.

The instance

CommandWhat it does
settingsEvery setting, and where each value comes from: a live override, .env, or the default.
set <key> <value>Change one live setting. default falls back to .env.
notice "TEXT" [--warn]Tell everyone currently connected something.
kernctl settings
kernctl set registration closed
kernctl set registration default
kernctl notice "Upgrading at 20:00; sync will pause briefly" --warn

Live settings take effect immediately, with no restart and nobody disconnected, because the sign-up checks and /instance read the live value rather than the environment. Which settings are live is on the configuration page.

A notice reaches open apps over the realtime socket, so it is the thing to send before an upgrade, not after.

Tokens and pairing

CommandWhat it does
token listEvery management token, its scope and when it was last used.
token new "NAME" [--read] [--days N]Mint one. Shown once, in full.
token revoke <id>Stop one working.
pair ["NAME"]An address and a token for Kern Platform, as one pasteable line.

A token reads kern_<id>_<secret>, and only the hash of the secret half is stored — a stolen database yields no working tokens. Scopes are full and read; give a monitoring check a read token and it can look at everything and change nothing.

kernctl token new "monitoring" --read --days 90
kernctl token list
kernctl token revoke a1b2c3d4e5f60718
kernctl audit 50

Everything that changes something is recorded in the audit trail with the token that did it, so token new per purpose — rather than one shared token — is what makes the audit trail worth reading.

kernctl pair prints a line beginning kern-platform:. Paste it into Kern Platform and it decodes the address and token, vets the address and stores the token in your operating system's credential store. Over a tunnel, forward the port first — the address in the line is the one the server knows itself by.

kernctl pair --json gives the same values as JSON, for a script.

Running it

CommandWhat it does
service status|start|stop|restart|logs|upgradeThe service itself, whether it runs under Docker or systemd.
backup [FILE]pg_dump, gzipped.
restore <FILE>Replace the database from a backup.
use <url> [--token T]Remember which server to manage.
version · helpItself.
kernctl service status
kernctl service upgrade
kernctl backup /srv/backups/kern-$(date +%F).sql.gz

service and backup act on the machine they run on, so they need a shell on the server — unlike everything else here, a tunnel is not enough. restore replaces the database: see backups for what clients do afterwards.

Scripting and exit codes

Formatted tables use python3 when it is there. --json always works and needs neither, which is what you want in a script.

Exit codeMeans
0Fine
1Refused by the server
2Called wrongly
3Couldn't reach the server
4health found a failure

1 and 3 are worth separating in a script: refused means the server is up and said no, unreachable means it isn't answering at all.

Monitoring

The whole check is one line, and needs no parsing:

kernctl health --json >/dev/null || echo "kern unhealthy"   # exit 4 when failing

Nightly backup with two weeks kept, from cron:

0 3 * * * /usr/local/bin/kernctl backup /srv/backups/kern-$(date +\%F).sql.gz && \
          find /srv/backups -name 'kern-*.sql.gz' -mtime +14 -delete

Give the monitoring check its own --read token, so a compromised monitoring box can't change anything, and so the audit trail shows which token did what.

For a load balancer, use the public endpoint instead: GET /kern/v1/health needs no token, checks the database and reports realtime figures.

Kern Platform

Kern Platform is the same management API in a desktop app: health, people, spaces, streams, settings, logs, the audit trail and a read-only SQL console. Anything one client can do, the other can — that is deliberate, and it is why a server with no graphical session is not limited.

To connect it, on the server:

kernctl pair "Kern Platform"

Paste the kern-platform: line into the app. If the server is remote, forward the port over SSH first and keep the tunnel open while you use it.