vcon-server

Command line

Every vcon-server command: what it does, what it prints, its exit codes, and which may run beside a serving server.

Every command but mcp takes --config <file>, the same file serve reads, and acts on the data directory it names. Like serve, each turns core dumps off and locks its memory before it reads a key, so it needs the same memory-lock allowance (see getting started). Run each as the service account that owns the data directory, never as root.

With the objects in an S3 bucket (storage.objects = "s3"; see keep vCons in an S3 bucket), every command but mcp and rekey master (which touches only server.sqlite) also reads the secret key file, reaches the bucket, reads its versioning and checks the claim on its prefix before it does anything else, and exits 1 with the storage cannot be used: and the reason when one of these fails. So the bucket must be reachable from the host where the commands run, as from the server. check reads every object, one request for each version, and backup copies every object from the bucket into files.

Serving

CommandPrintsRuns beside serve
serve --config <file>log lines on standard error; nothing on standard outputno

serve runs the server until SIGTERM or SIGINT, then exits 0. It refuses to start, with exit code 1 and the reason, when the configuration, the master key, the data directory, the certificate or the storage cannot be used. Getting started runs it under systemd, and operations lists the lines it logs.

Output and exit codes

  • Each command prints what the matching admin route answers: one JSON object per line, so the output can be piped to jq or read line by line. A command with nothing to report prints nothing.
  • 0: done.
  • 1: refused or failed. Nothing is printed on standard output (except by check, which prints its report first); standard error says why, as vcon-server: <reason>.
  • 2: the command line does not parse, such as a missing --config or --override-holds without --force.

Tenants

CommandPrintsRuns beside serve
tenant create <name> [--retention <period>]{"id"}no
tenant list{"id", "name", "retention", "metadata"} per tenant, by nameyes
tenant set-retention <id> <period or default>{"id", "retention"}yes
tenant set-metadata <id> <none, counts or full>{"id", "metadata"}yes
tenant remove <id> [--force [--override-holds]]nothingno

tenant remove without --force removes only a tenant that keeps no version. --force also removes one that does, unless a uuid of it is under a legal hold. --override-holds destroys even held vCons; it exists only here, never over HTTP, and only with the server stopped.

tenant set-metadata sets which clear columns the tenant's Parquet exports carry: none (the default), counts or full. See export to Parquet.

Keys

CommandPrintsRuns beside serve
key create --tenant <id> --scope <s> [--scope <s>...] --label <label>{"id", "token"}yes
key create --operator --label <label>{"id", "token"}yes
key list [--tenant <id>]{"id", "tenant", "scopes", "label", "created_at", "revoked_at"} per key, oldest firstyes
key revoke <id>nothingyes

A token is printed once and never again; the server keeps only its hash. key create --operator is the only way to make an operator key, which is what the admin routes need. key list without --tenant lists every key, operator keys included (their tenant is null).

Audit logs

CommandPrintsRuns beside serve
audit export --admin [--since <seq>]the admin log's recordsyes
audit export --tenant <id> [--since <seq>]the tenant's log's recordsyes

Records are written oldest first, after --since if given; keep the last seq you read and pass it next time. See audit logs for the fields.

Key rotation

CommandPrintsRuns beside serve
rekey tenant <id>nothingno
rekey master --new-master <file>{"master_key_id", "next"}no

rekey tenant gives the tenant a new key: every stored version's data key is rewrapped under it, in batches, and the tenant's catalog is re-encrypted under it, so the old key opens nothing the tenant keeps. Bodies are not re-encrypted. A run that stops, for any reason, is finished by running it again.

rekey master rewraps every tenant key, and every key a stopped rekey tenant left pending, under the master key in --new-master. It works on a copy of server.sqlite under the new key and renames the copy over the original only once it is complete, so until then the original is untouched. A run stopped before the rename starts again; one stopped after it has nothing left to do. Afterwards, point master_key in the configuration at the new file; the old key no longer opens anything. The full procedure is in operations.

Checking

CommandPrintsRuns beside serve
checkone line per tenant, then a summaryno

check is the one command that prints its report and still exits 1, when it found a problem: the report says what. See checking the data for each field.

Backup

CommandPrintsRuns beside serve
backup <dir>{"tenants", "tries", "versions"}yes

backup writes a consistent copy of the data directory to <dir>, which must not exist yet, while the server runs. See back up and restore for what it copies, how to restore the copy, and what to keep apart from it.

Export

CommandPrintsRuns beside serve
export parquet --tenant <id> --to <dir or s3://bucket/prefix> [--since <time>] [--until <time>]{"tenant", "metadata", "to", "manifest", "seq", "files", "rows", "bytes", "skipped", "unread"}yes

export parquet writes a copy of the tenant's kept versions, current and hidden, as Parquet files under tenant=<id>/date=<yyyy-mm-dd>/, and then a manifest, tenant=<id>/manifest/<seq>.json, that names them. It reads the store and changes nothing in it but one export record in the tenant's audit log. --since and --until are RFC 3339 times; a version stored at or after --since and before --until is exported. See export to Parquet.

Storage

CommandPrintsBeside a server
storage move-objects{"tenant", "moved", "missing"} per tenant, then {"moved"}no

storage move-objects moves every tenant's sealed objects from the data directory to the bucket that storage.objects = "s3" names (see keep vCons in an S3 bucket). Each object is written to the bucket, read back and compared byte for byte before its file is removed, so a run that stops loses nothing: run it again to move the rest. missing counts the versions check finds without an object afterwards. It is refused when the configuration keeps the objects in the data directory.

MCP for an AI agent

CommandPrintsRuns beside serve
mcp --config <client file>MCP messages on standard outputyes

mcp is for an AI agent that starts its MCP servers as commands. It reads MCP messages on standard input, sends each to a running server's /mcp with one API key, and writes the server's answers to standard output, so the agent reaches the same tools, scopes and audit log as over HTTPS. It reads a client file instead of the server's configuration, and it opens no data directory and no master key, so it can run on another machine. It stops with exit code 0 when standard input ends.

url = "https://vcon.example.com:8443/mcp"
key_file = "/home/agent/.config/vcon-server/agent.token"
ca = "/home/agent/.config/vcon-server/ca.pem"
  • url: the server's MCP address. It must start with https://.
  • key_file: a file holding one API token and nothing else. The token is never on the command line or in the environment.
  • ca: optional. PEM certificates to trust for the server, such as a private CA's; without it, the system's trusted roots are used.

The command connects directly, through no proxy. It exits with code 1 when the client file or the key file cannot be read, when the server cannot be reached, or when the server refuses the key: vcon-server: the server refused the key (401). Like the other commands it locks its memory, so the account that runs it needs the same memory-lock allowance.

Running beside the server

Commands marked "no" refuse while serve, or any other command, holds the data directory, with exit code 1 and the data directory is in use; stop the server and any other command first. The others run beside the server, and the server sees their changes at its next request: a revoked key is refused at once. See operations.

A first tenant, from nothing

sudo -u vcon vcon-server tenant create acme --config /etc/vcon-server/vcon-server.toml
# {"id":"3f0c…"}
sudo -u vcon vcon-server key create --operator --label ops --config /etc/vcon-server/vcon-server.toml
# {"id":"…","token":"vcs_…"}   keep this token: it manages tenants over HTTP
sudo -u vcon vcon-server key create --tenant 3f0c… --scope write --label recorder-1 \
  --config /etc/vcon-server/vcon-server.toml
# {"id":"…","token":"vcs_…"}   give this one to the producer

Source of this page: website/content/docs/command-line.md