The sumimi CLI

sumimi is one self-contained executable with DuckDB compiled in — no shared library to install alongside it. The same program is the server (sumimi serve …) and the analyser (sumimi summary …); this page is about the analyser half. It answers the same questions whether it reads a .duckdb file directly or talks to a running server over HTTP — so the same command works on your laptop with the file copied over, and on the box itself.

One binary, two modes

There is only one file to download. The release archive also ships sumimi-server and sumimi-client as symlinks to sumimi, for people who prefer the explicit name; the program switches mode by the name it was invoked under. sumimi-server serve and sumimi-client summary therefore both work, but they are the same executable.

I want to…Command
Run the serversumimi serve --db ./sumimi.duckdb --port 3000
Manage the admin accountsumimi admin --username admin
Print the resolved configurationsumimi config
Analyse a databasesumimi summary <website-id> --from 7d
Break the traffic downsumimi pages, sumimi referrers, sumimi browsers, sumimi os, sumimi devices, sumimi countries, sumimi languages, sumimi screens
Look at the raw recordssumimi events, sumimi sessions, sumimi timeseries
Export a reportsumimi export <website-id> pages --format csv
Fill a database with demo trafficsumimi seed --db ./sumimi.duckdb
Enforce retention / delete a sitesumimi prune, sumimi erase
Store the credentials for a serversumimi login <url>

The serve, admin and config subcommands belong to the server half and are described in the install guide and the self-hosting guide. Everything else on this page is the analyser.

Two sources, one interface

Pick exactly one source. Most subcommands work in both modes.

ModeFlagsNotes
Local file --db <path>
(default ./sumimi.duckdb)
The file is opened read-only. prune, erase and seed open it read-write.
HTTP API --url http://host:3000 plus --username/--password or --token Logs in as the admin account via POST /api/login and caches the token in ~/.config/sumimi/credentials.json (mode 0600).

Three subcommands are local-only, because they touch the database file itself rather than the data in it: seed (writes demo traffic), prune (enforces the retention window) and erase (deletes a website). And websites needs a running server — ask it over HTTP.

# read a database file
sumimi summary --db /var/lib/sumimi/sumimi.duckdb 3f2a...

# or talk to a running server
sumimi summary --url http://127.0.0.1:3999 --username admin --password '...' 3f2a...

# with a token you already have
sumimi summary --url http://127.0.0.1:3999 --token eyJhbGciOi... 3f2a...

In HTTP mode the command asks the server for the numbers; in local mode it runs the same queries against the file directly. The two are not approximations of each other — they are the same query code, so the numbers agree by construction rather than by convention.

Common flags

FlagMeaning
--db <path>The database file to read (local mode). Default ./sumimi.duckdb.
--url <url>Base URL of a running server (HTTP mode).
--token <token>Bearer token for HTTP mode. Bypasses the credential cache.
--username <name>Admin username for HTTP mode.
--password <pass>Admin password for HTTP mode.
--jsonMachine-readable output instead of a table.
--from <date>Start of the window. A relative range (7d, 24h, 30m), a date (2026-01-01), a timestamp (2026-01-01T10:00:00) or an RFC 3339 timestamp (2026-01-01T10:00:00Z).
--to <date>End of the window. Same format as --from.
--limit <n>How many rows to show, on the row-based reports. Default 50.

The website id is a positional argument, not a flag: sumimi pages 3f2a.... --from, --to and --limit come after it. summary takes the website id and the window but no --limit; websites takes no arguments at all.

The source flags have environment equivalents: SUMIMI_DB, SUMIMI_URL, SUMIMI_TOKEN, SUMIMI_ADMIN_USER and SUMIMI_ADMIN_PASSWORD.

Commands

websites

List the websites known to a running server.

sumimi websites --url http://127.0.0.1:3999 --username admin --password '...'
ID                                    NAME          DOMAIN       CREATED
------------------------------------  ------------  -----------  --------------------------
3f2a1b4c-5d6e-7f80-9a1b-2c3d4e5f6071  My Blog       blog.example.com  2026-01-12
7c8d9e0f-1a2b-3c4d-5e6f-708192a3b4c5  My Shop       shop.example.com  2026-02-03

summary

The headline numbers for a window: page views, visitors, visits, bounces and average visit duration.

sumimi summary --db ./sumimi.duckdb 3f2a... --from 7d
METRIC              VALUE
------------------  -----
Pageviews           1523
Visitors            842
Views                611
Bounces              233
Visit duration (s)    84

pages

The most visited paths.

sumimi pages --db ./sumimi.duckdb 3f2a... --limit 5
NAME                                VALUE
----------------------------------  -----
/posts/hello                          312
/posts/world                          254
/pricing                               98

referrers

Where the traffic came from.

sumimi referrers --db ./sumimi.duckdb 3f2a...
NAME                             VALUE
-------------------------------  -----
(direct)                         223
https://www.google.com/          213
https://duckduckgo.com/          104

Visits with no referrer are counted as (direct) rather than unknown.

browsers

The browser breakdown. The operating system, device, country, language and screen breakdowns each have their own subcommand: os, devices, countries, languages and screens. They take the same arguments and print the same shape of table.

sumimi browsers  --db ./sumimi.duckdb 3f2a...
sumimi os       --db ./sumimi.duckdb 3f2a...
sumimi devices  --db ./sumimi.duckdb 3f2a...
sumimi countries --db ./sumimi.duckdb 3f2a...

events

The custom events, grouped by name and counted — the same breakdown the /stats/events endpoint returns.

sumimi events --db ./sumimi.duckdb 3f2a...
NAME                  VALUE
--------------------  -----
newsletter_subscribe  34
signup                23
download              8

To read the individual event records — with their properties — use the API's /api/websites/:id/events endpoint, which returns the raw log rather than the breakdown.

sessions

The session log, newest first.

sumimi sessions --db ./sumimi.duckdb 3f2a... --limit 3

timeseries

Page views over time, hourly or daily depending on the window.

sumimi timeseries --db ./sumimi.duckdb 3f2a... --from 30d
DATE                  VIEWS
--------------------  -----
2026-10-25T00:00:00Z    31
2026-10-26T00:00:00Z    68
2026-10-27T00:00:00Z   102

export

Export the data of one website as JSON or CSV.

sumimi export --db ./sumimi.duckdb 3f2a... pages --format csv > traffic.csv
sumimi export --db ./sumimi.duckdb 3f2a... summary --format json > traffic.json

The report comes after the website id and names what to export: summary, pages, referrers, browsers, events, sessions or timeseries. The format is json (the default) or csv.

Write the output to a file rather than to the terminal when you are exporting a large window; the table format is for reading, the export format is for feeding into something else.

Event properties are yours

Custom event properties are free-form data that you supplied when tracking. The CLI prints them verbatim, and an export writes them to a file. If they contain personal data, that is personal data in your database — see the privacy page.

seed

Fill a database with synthetic demo traffic, so you can look at the dashboard — or a demo of it — without pointing a real site at it. The data is entirely invented: invented domains, invented visitors, an invented traffic shape.

sumimi seed --db ./sumimi.duckdb --days 30 --visitors 140 \
  --website "Example Blog" --domain example.com
seeded 3992 sessions and 724 events
website: e00f7a9b-ab5c-46ac-8293-f28541b85172
start the server and open the dashboard to see it
FlagDefaultMeaning
--db <path>./sumimi.duckdbThe database file to write to. Created if it does not exist.
--days <n>30How many days of history to generate.
--visitors <n>120Roughly how many visitors per day.
--website <name>Example BlogThe name of the website to seed. It is created when missing.
--domain <domain>example.comThe domain of the seeded website.

The generated traffic is not uniform noise: pages, referrers, countries and browsers follow weighted distributions, some visitors come back during the same day, weekends dip below weekdays, and a handful of custom events are recorded along the way.

Stop the server first

seed writes directly to the database file, and DuckDB allows only one writer per file. Stop the server before seeding it, and start it again afterwards.

prune

Enforce the retention window: delete sessions and events older than the configured number of days. 0 days means keep everything, so nothing is deleted.

# local file — opened read-write for this command only
sumimi prune --db /var/lib/sumimi/sumimi.duckdb 3f2a...

This one is local-only: the server has no pruning endpoint yet, so prune works on the database file and nowhere else. Stop the service, prune the file, start the service again — never point the CLI at a database file the server currently has open.

erase

Delete a website and all of its data. This is the "right to be forgotten" button: erase removes the website, its sessions and its events.

sumimi erase --db /var/lib/sumimi/sumimi.duckdb 3f2a...

It asks for confirmation unless --yes is given. Like prune, this is local-only. To erase a website while the server is running, call DELETE /api/websites/:id against the API instead.

channels, regions, cities, hosts, tags

The other breakdowns: the traffic channel (direct, Search, Social, email, paidAds, referral, affiliate, sms, llm, Shopping, Video), the region and the city of the visitor, the hostname of the site and the tag of a custom event. They take the same arguments as the other reports.

sumimi channels --db ./sumimi.duckdb 3f2a...
sumimi regions  --db ./sumimi.duckdb 3f2a...
sumimi cities   --db ./sumimi.duckdb 3f2a...
sumimi hosts    --db ./sumimi.duckdb 3f2a...
sumimi tags     --db ./sumimi.duckdb 3f2a...

sumimi query-parameters shows the query parameters of the visited pages, counted by name and by value.

sumimi query-parameters --db ./sumimi.duckdb 3f2a...

uptime

Check whether a website is up. When the site does not answer, two references are asked as well — bing.com and 8.8.8.8:53 — so "the site is down" and "this machine has no internet" are not confused.

# is it up?
sumimi uptime https://example.com

# watch more endpoints of a website
sumimi uptime --website 3f2a... --add https://example.com/health
sumimi uptime --website 3f2a... --list
sumimi uptime --website 3f2a... --remove https://example.com/health

# check every endpoint of a website now
sumimi uptime --website 3f2a... --check-all

The checks are recorded in the instance store (or sent to the API when --url is used). A failed check says whether the network was the problem.

service

Run the server as a service — no systemd, no root, no init script. The server runs under a supervisor: a second process that owns it and restarts it when it dies. Everything lives inside the instance directory, so the service runs as the user who installed it.

# start it, with a domain: the certificate is issued for it
sumimi service start --dir ~/sumimi \
  --domain stats.example.com --email you@example.com

# what is it doing?
sumimi service status --dir ~/sumimi

# stop it — the writer flushes and the instance store is checkpointed
sumimi service stop --dir ~/sumimi

# stop it and start it again, with the same arguments
sumimi service restart --dir ~/sumimi

The flags of start are the server's own: --domain, --email, --port, --host and --ssl-port, alongside the global --dir, --db and --backend. The server's output goes to <dir>/service/log.

$ sumimi service status --dir ~/sumimi
state:     running
pid:       41553 (supervisor)
server:    41572
uptime:    3602s
restarts:  0
port:      3000
domain:    https://stats.example.com/
instance:  /home/you/sumimi
log:       /home/you/sumimi/service/log

If the server keeps dying, the supervisor says so in restarts, and gives up after five failures in a row rather than restarting a broken thing forever. The self-hosting guide has the whole deployment.

ping

Ask a server for its heartbeat. The exit code is the answer: zero when it answered, one when it did not. This is the health check of a container.

$ sumimi ping --url http://127.0.0.1:3000
http://127.0.0.1:3000 is up (version 0.4.1)

The MCP server

sumimi mcp speaks the Model Context Protocol: JSON-RPC 2.0 over stdio, the way an MCP client expects. It offers the analytics as three tools — the numbers, a question, and a way to record an event:

toolwhat it does
sumimi_report Run a report — the summary, the top pages, the traffic sources, the breakdowns, the sessions, the time series — and get the numbers as JSON.
sumimi_record Record a page view or a custom event, through the same endpoint the tracker posts to.
sumimi_ask Ask a question in plain language and get the answer computed from the database.
# against a running server
sumimi mcp --url http://127.0.0.1:3999 --token '...'

# against a local database (read-only when a server holds it)
sumimi mcp --db ./sumimi/data.duckdb

# register it with a client
claude mcp add sumimi -- sumimi mcp --url http://127.0.0.1:3999 --token '...'

There are no write tools: nothing creates a website, changes a setting, prunes data or erases anything. The server can look, count and record — and nothing else. Every number it returns comes from the same reports the dashboard runs, so the agent and the owner cannot see different numbers.

JSON output

Every command accepts --json, which is what you want in a script:

sumimi pages --db ./sumimi.duckdb 3f2a... --json | jq '.[0].url'
[
  {
    "name": "/posts/hello",
    "url": "/posts/hello",
    "value": 312
  },
  {
    "name": "/posts/world",
    "url": "/posts/world",
    "value": 254
  }
]

Credentials

In HTTP mode, when you give --username and --password, the token is cached in ~/.config/sumimi/credentials.json with mode 0600, so later commands can be run without repeating the password. Pass --token to bypass the cache entirely.

sumimi summary --url http://127.0.0.1:3999 --username admin --password '...' 3f2a...
sumimi summary --url http://127.0.0.1:3999 3f2a...   # uses the cached token

The login subcommand does the same thing up front: it checks the credentials against the server and stores them.

sumimi login http://127.0.0.1:3999 --username admin
# the password is asked for interactively when --password is omitted
credentials saved to /home/you/.config/sumimi/credentials.json
logged in to http://127.0.0.1:3999

Safety rules

  • The CLI opens a local database read-only, except for prune, erase and seed.
  • It never writes to a database the server currently has open.
  • Destructive operations work on the database file, so they need the server to be stopped. To delete a website while the server is running, use the API (DELETE /api/websites/:id), which is subject to the same authorisation as everything else.
  • Output is truncated to --limit rows (default 50, capped at 1000) so a stray command cannot flood your terminal. export ignores --limit and asks for up to 10000 rows.

Examples

Yesterday's top pages

sumimi pages --db ./sumimi.duckdb 3f2a... --from 2026-01-14 --to 2026-01-15

A weekly report as CSV

sumimi export --db ./sumimi.duckdb 3f2a... pages --format csv --from 30d > last-30-days.csv

Counting unique visitors per day

sumimi timeseries --db ./sumimi.duckdb 3f2a... --from 7d --json | jq 'map(.value)'

Auditing a running server

sumimi websites --url http://127.0.0.1:3999 --username admin --password '...'
sumimi summary --url http://127.0.0.1:3999 --username admin --password '...' 3f2a... --from 30d

A demo database to look at

# the server must be stopped while the database is written
sumimi seed --db ./demo.duckdb --days 30
sumimi serve --db ./demo.duckdb --port 3000

The sumimi CLI is original work, Apache-2.0 licensed together with the rest of Sumimi. All licences are recorded in assets/CREDITS.md.