Skip to content

yarctl ​

Unified operator CLI for yarilo. Three top-level planes:

PlaneTalks toWhat
directoryarilo-director :9103ring, backends, per-user routing
backendyarilo-backend-api :9105per-account storage state: dict, acl, quota, folder, user, index, mdbox, subscriptions, specialuse, metadata, mailbox, who, sessions, fts, warden
authyarilo-auth master socketauth cache, SCRAM verifiers

The director and backend planes speak JSON over HTTP with Bearer-token auth; an IP allow-list can be added on top. The director API is always plain HTTP. The backend API serves HTTPS when internal TLS is on; the chart then sets YARILO_ADMIN_TLS_* for yarctl, and the --tls-* flags do the same by hand. See Director API and Backend API for the wire references.


Quick start ​

sh
# exec into the director pod
kubectl exec -it <director-pod> -- yarctl director status

# or from outside the cluster (set URL + token explicitly)
yarctl --url http://10.0.0.1:9103 --token <token> director status

Configuration ​

The chart sets the environment in every pod that carries yarctl, so no flags are needed there. What a pod can reach depends on its type:

  • The director pod has no YARILO_ADMIN_TYPE, so every plane is available.
  • Backend, yarilo-backend-api and login pods set YARILO_ADMIN_TYPE=backend. There the backend prefix is implicit, and yarctl director … is refused.

The variables:

VariableDefaultDescription
YARILO_ADMIN_URLhttp://localhost:9103Director API base URL (used by director subcommand)
YARILO_ADMIN_TOKEN—Director Bearer token (fallback: DIRECTOR_API_TOKEN)
YARILO_BACKEND_API_URLhttp://localhost:9105Backend API base URL (used by backend <service> subcommands)
YARILO_BACKEND_API_TOKEN—Backend API Bearer token (fallback: BACKEND_API_TOKEN)
YARILO_BACKEND_API_PORT9105Backend API port on the pod a per-user operation is routed to
YARILO_ADMIN_ROUTE_BY_USERautoRoute per-user backend operations to the user's pod through a director LOOKUP
YARILO_ADMIN_TYPE—backend, director or auth: the plane this container serves; the plane word becomes optional
YARILO_API_URL, YARILO_API_TOKEN—URL and token for the plane named by YARILO_ADMIN_TYPE; override the plane-specific variables
YARILO_ADMIN_TLS_CERT, _KEY, _CA, _SERVER_NAME—Client certificate, key, CA and server name for HTTPS to the backend API

With YARILO_ADMIN_ROUTE_BY_USER=auto, routing is on when a director URL is configured, through YARILO_ADMIN_URL or --url. Each per-user backend command then goes to the pod that owns the user, so it never becomes a second writer of that user's index. With no director URL, every command goes to --backend-url.

To read the auto-generated tokens from outside the pod:

sh
kubectl get secret yarilo-director-api-token -o jsonpath='{.data.token}' | base64 -d
kubectl get secret yarilo-backend-api-token   -o jsonpath='{.data.token}' | base64 -d

Global flags ​

yarctl [--url URL] [--token TOKEN] \
             [--backend-url URL] [--backend-token TOKEN] \
             <resource> <action> [args...]

Global flags (-O, --url, --token, --backend-url, --backend-token, …) are position-independent — they may appear before the plane, between the plane and the command, or trailing. All of these are equivalent:

sh
yarctl -O json director ring status
yarctl director ring status -O json
yarctl director -O json ring status
FlagDefaultUsed byDescription
--url$YARILO_ADMIN_URL or http://localhost:9103directorDirector API base URL
--token$YARILO_ADMIN_TOKEN or $DIRECTOR_API_TOKENdirectorDirector Bearer token
--backend-url$YARILO_BACKEND_API_URL or http://localhost:9105backend <service>Backend API base URL
--backend-token$YARILO_BACKEND_API_TOKEN or $BACKEND_API_TOKENbackend <service>Backend API Bearer token
--backend-port$YARILO_BACKEND_API_PORT or 9105backend <service>Backend API port when routing by user
--route-by-user$YARILO_ADMIN_ROUTE_BY_USER or autobackend <service>auto, on or off
--tls-cert, --tls-key, --tls-ca, --tls-server-name$YARILO_ADMIN_TLS_*backend <service>HTTPS to the backend API
-Ohumanallhuman or json; see Output

The two URLs are separate by design: director-plane ops (ring, backends, users, peers) live on yarilo-director:9103; backend-plane ops (dict / acl / quota / folder / user / mailbox) live on yarilo-backend-api:9105. Each binary holds the state its plane exposes — director state lives in director's process; backend state (NFS + dicts) lives on backend pods.


Command index ​

Every family the tool exposes, and where each is documented. The tool's own usage text is checked against its dispatcher by a test in the repository, and this index is written from that usage — so a family the tool announces is a family this page can list. Nothing checks the page itself: a row missing here is a gap, not a build failure. yarctl is organised as planes — director, backend, auth — with a few shorthands that skip the prefix.

FamilyWhat it managesDocumented
directorring, backends, per-user routingbelow
backend dictkey-value store operationsbelow
backend aclRFC 4314 access controlbelow
backend quotaRFC 9208 counters and clonesbelow
backend folderlisting, GUIDs, repair, create/rename/deletebelow
backend useruserdb queries, per-folder usage, enumerationbelow
backend indexindex dump, rebuild, fold, cache purgebelow
backend mdboxmap purge, alt-storage movesbelow
backend subscriptionsIMAP SUBSCRIBE statebelow
backend specialuseRFC 6154 special-use attributesbelow
backend metadataRFC 5464 annotationsbelow
backend mailboxmessage retrieval — the content of a stored messagebelow
backend whoactive sessionsbelow
backend sessionskick a session by idbelow
backend ftssearch index status, rescan, optimize, lookupbelow
authauth-cache flush, SCRAM verifier generationbelow
backend wardenconnection accounting dumpbelow
waitblock until endpoints answerbelow

Three families answer without the plane prefix: user and warden are shorthands for their backend equivalents, and wait belongs to no plane. subs is an alias for subscriptions inside the backend plane. With YARILO_ADMIN_TYPE=backend, every backend family answers without the prefix.

Run any family with no command to get its usage; the text there is the same one this page documents.

Commands ​

director status ​

Every backend in the ring, with its tag, state, weight and session count. Ring membership is under director ring status.

sh
yarctl director status

director dump ​

Full state as JSON: backends, user→backend assignments, ring members and session records. See Director API for the fields.

sh
yarctl director dump

director map ​

Show user→backend assignments. Without --user it lists every assignment. With --user it reads that user's stored assignment, or reports "pinned": false, and changes nothing.

sh
yarctl director map
yarctl director map --user [email protected]

director backends list ​

List all backends in the ring.

sh
yarctl director backends list

director backends add ​

Add a backend to the ring.

sh
yarctl director backends add <ip> --port <port> [--tag <tag>] [--vhosts <n>]
sh
yarctl director backends add 10.0.0.3 --port 993 --tag ssd
yarctl director backends add 10.0.0.4 --port 993 --tag ssd --vhosts 200

director backends remove ​

Remove a backend from the ring.

sh
yarctl director backends remove <ip>
sh
yarctl director backends remove 10.0.0.3

director backends update ​

Update the virtual node weight of a backend.

sh
yarctl director backends update <ip> --vhosts <n>
sh
yarctl director backends update 10.0.0.3 --vhosts 200

director backends up ​

Mark a backend as up (resumes routing to it).

sh
yarctl director backends up <ip>

director backends down ​

Mark a backend as down: new logins stop routing to it, existing sessions continue.

sh
yarctl director backends down <ip>

director backends flush ​

Evacuate a backend, or every backend with all: its users are kicked and re-route to the surviving backends.

sh
yarctl director backends flush <ip|all> [--force] [--max-parallel N]

By default this is a graceful drain that moves at most max_parallel_moves users at a time. --force kicks every session at once. --max-parallel sets the window for this run.

sh
yarctl director backends flush 10.0.0.3
yarctl director backends flush all --force

director users move ​

Assign a user to a specific backend and kick the user's sessions on the old one. The assignment is a sticky pin that expires after user_expire like any other.

sh
yarctl director users move <user> --backend <ip:port>
yarctl director users move <user> --ip <ip> --port <port>
sh
yarctl director users move [email protected] --backend 10.0.0.1:993

director users kick ​

Kick a user: the sticky assignment is cleared and every session of the user ends, after user_kick_delay (default 2 s).

sh
yarctl director users kick <user>
sh
yarctl director users kick [email protected]

director domains move ​

With assignment_policy: domain, move a domain to another backend of its tag and kick the domain's sessions on the old one. The backend is named by its IP. Users of the domain without a live session follow at their next login: their sticky entries are not trusted once they no longer name the domain's backend.

sh
yarctl director domains move <domain> <backend-ip>
sh
yarctl director domains move example.com 10.0.0.2

director ring status ​

Show the ring topology as the queried replica sees it (ring membership is per-replica, so this is one replica's own view). For every member it prints the computed left/right neighbors in (ip,port) order, and for this replica's direct neighbors the live edge — role (left/right, or both at N=2 where a single connection serves both directions), state (connected/reconnecting) and uptime. It also prints each member's dedup watermark (highest seq processed from that origin; - when none has been heard) and any tombstones (members known dead on this replica, with the tombstone age).

sh
yarctl director ring status
ring status: 3 directors (self 10.0.0.2:9102, backend-set 1a2b3c4d)
IDX  ADDR              LEFT | RIGHT                       LINK                  SEQ
0    10.0.0.1:9102     10.0.0.3:9102 | 10.0.0.2:9102      left connected 4m12s  41
1  * 10.0.0.2:9102     10.0.0.1:9102 | 10.0.0.3:9102      (self)                42
2    10.0.0.3:9102     10.0.0.2:9102 | 10.0.0.1:9102      right connected 4m12s 40

backend-set is a hash of this replica's routing backend set; replicas that agree on routing print the same value.

Use -O json for the structured object (schemaVersion, self, size, members[], tombstones[], backendSetHash) — suitable for programmatic topology assertions.

Cross-replica view ​

Because ring membership is per-replica, --all makes the queried director aggregate every replica's own view server-side (one authorized fan-out to peers' admin APIs) and returns a matrix plus a healthy verdict:

sh
yarctl director ring status --all
ring topology: UNHEALTHY (3 replicas, 1 issue)
REPLICA           REACHABLE  SIZE  BACKEND-SET  SELF-NEIGHBORS (L | R)
10.0.0.1:9102     yes        3     1a2b3c4d     10.0.0.3:9102 | 10.0.0.2:9102
10.0.0.2:9102     yes        3     1a2b3c4d     10.0.0.1:9102 | 10.0.0.3:9102
10.0.0.3:9102     no         -     -            -
issues:
  [error] peer-unreachable: 10.0.0.3:9102 is in membership but its view could not be collected
assumptions:
  - peer API endpoints derived as <ring-ip>:9103 — assumes uniform api.listen across replicas
  - admin API is plain HTTP guarded by Bearer token + api.allowed_nets; fan-out source is a director pod IP

The verdict flips to UNHEALTHY on error-severity issues — peer-unreachable (a member whose view could not be collected — never reported as healthy), view-size-mismatch, backend-set-divergence (replicas hashing their routing backend set differently), asymmetric-edge (A.right=B but B does not see A as its left), and tombstone-divergence. seq-lag is warn-only and does not fail the verdict (watermarks legitimately differ during activity). -O json returns {schemaVersion, healthy, issues[], replicas[], assumptions[]}.

Precondition: the fan-out derives each peer's API endpoint from its ring IP plus this replica's api.listen port, so all directors must share the same api.listen (true for a Helm release sharing one ConfigMap), and api.allowed_nets (if set) must include the director pod CIDR.


director ring add and director ring remove ​

Both fail with 410 Gone and print why: ring membership is self-organizing. A director joins through its director_service.peers seed, and a dead member is removed by its neighbors. See Ring formation.


Backend plane ​

Everything under yarctl backend <service> talks to yarilo-backend-api (default http://localhost:9105, or $YARILO_BACKEND_API_URL). Override per invocation with --backend-url and --backend-token.

Commands that name a mailbox take --namespace NS (default personal). A command that writes says so in its Effect line; the rest only read.

backend dict ​

Direct access to the key-value stores the server keeps its non-mail state in — quota counters, metadata annotations, ACL indexes. An escape hatch for inspection and repair, not a routine tool: writing here bypasses the code that normally maintains these rows.

yarctl backend dict drivers
yarctl backend dict exists NAME
yarctl backend dict lookup [op-flags] NAME KEY
yarctl backend dict iterate [--recurse] [--no-value] [--exact] [--sort-key] [--sort-value] NAME PATH
yarctl backend dict set [--value-stdin] [op-flags] NAME KEY [VALUE]
yarctl backend dict unset [op-flags] NAME KEY
yarctl backend dict atomic-inc [op-flags] NAME KEY DELTA
yarctl backend dict expire-scan NAME
yarctl backend dict commit-batch [op-flags] NAME

NAME is a configured dict (metadata, quota, …); exists answers whether it resolves. Op-flags carry the identity a dict operation runs under: --user USER, --home DIR, --expire-secs N (default TTL for writes).

iterate streams NDJSON, so it is safe over a large prefix. commit-batch reads a TAB-delimited script from stdin — set\tKEY\tBASE64, unset\tKEY, atomic-inc\tKEY\tDELTA — and applies it as one batch.

sh
yarctl backend dict lookup metadata priv/box/abc123/comment
yarctl backend dict iterate --recurse --sort-key metadata priv/
yarctl backend dict atomic-inc quota priv/quota/storage 1024

Effect: set, unset, atomic-inc, expire-scan and commit-batch write; expire-scan drops rows whose TTL has passed. The rest read.

backend acl ​

Access control on mailboxes (RFC 4314), including the namespace-wide index that lists every entry a user has.

yarctl backend acl list   <user>
yarctl backend acl get    <user> <mailbox>
yarctl backend acl get    --root <user>
yarctl backend acl set    <user> <mailbox> <identifier> <rights>
yarctl backend acl set    --root <user> <identifier> <rights>
yarctl backend acl delete <user> <mailbox> [<identifier>]        # alias: rm
yarctl backend acl delete --root <user> [<identifier>]
yarctl backend acl rebuild     <user> [<folder> ...] [--all] [--dry-run]
yarctl backend acl materialise <user> <folder> [<folder> ...] [--apply]   # alias: materialize
yarctl backend acl registry list    <user>
yarctl backend acl registry rebuild <user> --namespace NS

--root targets the namespace root rather than a mailbox. An identifier prefixed with - is a negative entry (rights subtracted). set upserts one entry, replacing any existing line for the same identifier; delete without an identifier drops the whole file.

sh
yarctl backend acl set [email protected] Shared/Team [email protected] lrsw
yarctl backend acl get [email protected] Shared/Team

rebuild reseeds the per-user ACL index from the ACL files: a folder list merges into the index, --all replaces it. materialise and the registry commands are covered in Owner and shared namespaces and Backend API.

Effect: set and delete rewrite the mailbox's ACL file and the index. rebuild without --dry-run, materialise --apply and registry rebuild write.

backend quota ​

Quota counters (RFC 9208) as the server computes them from the index.

yarctl backend quota show   <user>
yarctl backend quota recalc <user> [--namespace NS]
yarctl backend quota clone list
yarctl backend quota clone get <backend> <user>

show reports usage and limits: storage in KiB and message count, each with a percentage. Limits come from the user's quota_rule in userdb when backend_api.auth_master_addr is configured; an unlimited value prints as -1 with a percentage of 0. recalc rescans every folder and rewrites the counters, which is the repair for a counter that has drifted. clone inspects a configured mirror; the mirror is advisory and show remains authoritative.

sh
yarctl backend quota show [email protected]
yarctl backend quota recalc [email protected]

Effect: recalc rewrites the stored counters. The rest read.

backend folder ​

Mailbox-level operations: listing, identity, sizes, repair, and the create / rename / delete trio.

yarctl backend folder list    <user> [--namespace NS]
yarctl backend folder info    <user> <folder> [--namespace NS]
yarctl backend folder guid    <user> <folder> [--namespace NS]
yarctl backend folder stats   <user> <folder> [--namespace NS]
yarctl backend folder repair  <user> <folder> [--namespace NS]
yarctl backend folder create  <user> <folder> [--namespace NS] [--special-use ATTR]
yarctl backend folder delete  <user> <folder> [--namespace NS]   # alias: rm
yarctl backend folder rename  <user> <old> <new> [--namespace NS] # alias: mv
yarctl backend folder expunge <user> <folder> [--namespace NS] [--uids 1,2,3]

guid prints the rename-stable identifier — the one MAILBOXID reports and the one a Sieve script targets with fileinto :mailboxid. stats is info plus on-disk totals. repair rebuilds the folder's index from what is on disk and compacts its log.

--special-use on create is personal-namespace only (RFC 6154). rename does not support INBOX. expunge without --uids drops every message flagged \Deleted in the folder.

sh
yarctl backend folder list [email protected]
yarctl backend folder guid [email protected] Archive
yarctl backend folder expunge [email protected] Trash

Effect: repair, create, delete, rename and expunge write; delete also removes the folder's ACL state. The rest read.

backend user ​

What the server knows about an account.

yarctl backend user info    <user>
yarctl backend user usage   <user>
yarctl backend user iterate                                       # alias: list

info prints the username, the resolved home, the effective mail and INBOX paths, the configured namespaces and — when backend_api.auth_master_addr points at yarilo-auth — the userdb block. An account userdb does not know prints user not found; a userdb that cannot be reached fails the command with 503. That distinction is the useful part when an account behaves as though it does not exist.

usage gives per-folder message and byte totals across every namespace. iterate enumerates every username the userdb backend can list, and answers 503 when no auth address is configured — enumeration is a userdb capability, not a local one.

sh
yarctl backend user info [email protected]
yarctl backend user usage [email protected]

Effect: none; all three read.

backend index ​

The per-folder index: what it holds, and how to rebuild it when it stops matching the storage underneath.

yarctl backend index dump            <user> <folder> [--namespace NS] [--limit N]
yarctl backend index rebuild         <user> <folder> [--namespace NS]
yarctl backend index rebuild-storage <user> [--namespace NS] [--restore-orphans]
yarctl backend index optimize        <user> <folder> [--namespace NS]
yarctl backend index optimize        <user> --all [--namespace NS]
yarctl backend index cache-purge     <user> <folder> [--namespace NS]
yarctl backend index check           <user> [--namespace NS] [--fix]
yarctl backend index rebuild-guid-store <user> [--namespace NS]

dump prints every record — UID, flags, modseq, size, GUID.

rebuild regenerates one folder's index from the storage on disk, keeping the UIDs of filenames the index already knows. It is for maildir and sdbox and answers 501 for mdbox, whose storage is folder-agnostic.

rebuild-storage is the mdbox equivalent, and it is the heavier tool: reconcile the shared map against the physical m.<N> files, reset every folder index to the surviving messages, recompute reference counts, and drop map records whose message is gone. It refuses to run on an incomplete scan or an unmounted alt tier. Run it with delivery to that account quiesced.--restore-orphans re-files unreferenced messages carrying an ORIG_MAILBOX tag back into their folder; off by default, because the tag proves the message was once there, not that it is lost now.

optimize folds the index log into the base index — no semantic change, safe while nothing is reading the folder. With --all it folds every folder of the account, and the per-user map where the driver keeps one.

check reads every folder of the account and reports index records whose size reads back as their own storage key, the trace of a record tail written at a width the index did not announce. It is read-only without --fix. With --fix, each such record's storage key, size and GUID are rebuilt from the message in storage. It prints the counts checked, shifted and repaired.

rebuild-guid-store writes the per-account GUID store from the folder indexes: one record per copy of a message, which is what a JMAP id resolves through. The store is derived; run this when it is missing, behind, or written by an older build.

cache-purge rewrites the message cache as a new generation holding only what live messages point at. The cache is append-only and never shrinks by itself; there is no automatic trigger, so this is an operator action.

sh
yarctl backend index dump [email protected] INBOX --limit 20
yarctl backend index optimize [email protected] --all

Effect: everything except dump and check without --fix rewrites index state.

backend mdbox ​

The two mdbox-specific storage operations.

yarctl backend mdbox purge   <user> [--namespace NS]
yarctl backend mdbox altmove <user> [--namespace NS] [--before RFC3339] [--reverse]

purge compacts the storage tree: every m.<N> file holding at least one zero-reference record is rewritten without those records, or unlinked when all of them are dead. The map is rewritten atomically, and folder indexes pointing at live records keep working without per-folder I/O.

altmove moves messages to the alt (cold) tier, or back with --reverse. --before limits it to messages whose internal date precedes an RFC 3339 timestamp. Requires storage.mdbox_alt_storage_path.

sh
yarctl backend mdbox altmove [email protected] --before 2025-01-01T00:00:00Z
yarctl backend mdbox purge [email protected]

Effect: both rewrite storage. purge is what actually reclaims the space an expunge released.

backend subscriptions ​

IMAP SUBSCRIBE state, through the same on-disk format and lock key the IMAP command uses — so an admin write is visible to a live session immediately.

yarctl backend subscriptions list    <user> [--namespace NS]
yarctl backend subscriptions add     <user> <folder> [--namespace NS]
yarctl backend subscriptions remove  <user> <folder> [--namespace NS]   # alias: rm
yarctl backend subscriptions migrate <user> --namespace NS [--apply]

migrate folds a namespace's old per-namespace subscription file into the user's own, because subscriptions follow the subscriber rather than the namespace. It is a dry run unless --apply is given.

Effect: add, remove, and migrate --apply write.

backend specialuse ​

RFC 6154 attributes — which folder is Sent, which is Trash — as overrides on top of the configured defaults.

yarctl backend specialuse list   <user>
yarctl backend specialuse get    <user> <folder>
yarctl backend specialuse set    <user> <folder> <attr>
yarctl backend specialuse delete <user> <folder>   # alias: del

get reports the resolved attribute and its source: override, default or none — which is the difference between a user's own choice and the server's configuration, and the thing to check when a client files into the wrong folder. delete drops the override so the default applies again.

Only the personal namespace carries overrides; the attributes do not extend to shared or public mailboxes.

sh
yarctl backend specialuse set [email protected] Sent '\Sent'

Effect: set and delete write.

backend metadata ​

IMAP METADATA annotations (RFC 5464) — the same entries a client reads with GETMETADATA, and the mechanism a Sieve script is bound to a mailbox through.

yarctl backend metadata list   <user> [<folder>] [--namespace NS] [--scope private|shared] [--as-user U]
yarctl backend metadata get    <user> [<folder>] --entry /private/comment [--namespace NS] [--as-user U]
yarctl backend metadata set    <user> [<folder>] --entry /private/comment --value 'literal' | --value-file PATH [--namespace NS] [--as-user U]
yarctl backend metadata delete <user> [<folder>] --entry /private/comment [--namespace NS] [--as-user U]   # alias: del

Entry names begin with /private/ or /shared/. An empty folder targets server scope, which is stored vendor-prefixed under INBOX's GUID.

--as-user defaults to the account being read and matters only for shared and public folders under /private/ scope, where every user has their own slice of the annotations — so reading "the" private entry of a shared mailbox is meaningless without saying whose.

get prints the value base64-encoded, because an annotation may hold bytes. set takes either --value or --value-file.

sh
yarctl backend metadata set [email protected] INBOX \
  --entry /shared/imapsieve/script --value 'onappend'

Effect: set and delete write.

backend mailbox ​

Retrieves the content of a stored message. The only family here that returns mail rather than metadata about it.

yarctl backend mailbox message get mime <user> <folder> --uid N | --guid G [--namespace NS] [--out FILE]
yarctl backend mailbox message get raw  <user> <folder> --uid N | --guid G [--namespace NS] [--out FILE]

mime prints the headers as written and each part's headers, with the part bodies elided — enough to see how a message is structured without reading it. raw prints the message byte for byte.

A message is addressed by exactly one of --uid within the folder, or --guid, which survives the message being moved. --out writes to a file instead of stdout.

Effect: reads only. No flag is set and no counter moves — a get here does not mark a message \Seen the way a client's FETCH would, which is what makes it safe to inspect a live mailbox.

Every call is recorded on the backend, because it returns the contents of someone's mailbox. Treat it as you would any other tool that reads user mail: reach for mime first, and for raw only when the bytes themselves are the question.

backend who ​

Who is connected right now. Reads the accounting yarilo-warden keeps, which is fed by the login and session processes.

yarctl backend who [list] [--protocol IMAP] [--user U] [--all] [--output table|json]
yarctl backend who count [proto] [--user U] [--by user|protocol] [--all] [--output table|json]

Output is a human-readable table by default; --output json for a machine. count aggregates instead of listing, grouped by user or by protocol. Both cover only this backend's sessions unless --all is given; on the listing, --all also adds a BACKEND column.

The session ids printed here are what backend sessions kick takes.

sh
yarctl backend who --protocol IMAP
yarctl backend who count --by user

Effect: none; both read.

backend sessions ​

yarctl backend sessions kick <sess-id> [--user U] [--protocols imap,pop3,...]

Closes one session. backend-api hands the kick to yarilo-warden, which emits it on every protocol's channel, and only the owner of that id reacts — so the id alone is enough, and it comes from backend who. Success means the kick was emitted, not that the session is confirmed closed.

--protocols narrows the broadcast (default: all four channels). --user is recorded for the audit log only; it does not select the session.

Effect: closes a live connection.

backend fts ​

The search index of an account.

yarctl backend fts status   <user> [--folder NAME]
yarctl backend fts rescan   <user> [--folder NAME]
yarctl backend fts optimize <user>
yarctl backend fts lookup   <user> [--folder NAME] [--header NAME:VALUE] [--body TEXT] [--text TEXT]

status prints a folder's indexing checkpoint (default INBOX). rescan reconciles the index against the mailbox, every folder when --folder is absent. optimize compacts every index the account owns, which otherwise happens in the background past fts_flatcurve_optimize_limit. lookup asks the index what an IMAP SEARCH would, with the query built the way SEARCH builds it; --header, --body and --text may repeat and are ANDed.

All four answer 501 when yarilo-backend-api has no fts_addr. See FTS for the index itself.

Effect: rescan and optimize write. The rest read.

auth ​

Queries and utilities against yarilo-auth.

yarctl auth cache flush [<user-mask> ...]
yarctl auth scram-verifier [--mech sha256|sha1] [--iterations N] [--password X]

cache flush evicts auth-cache entries matching the masks; no mask flushes everything. scram-verifier derives the verifier blob for a SQL password column — {SCRAM-SHA-256} by default. --iterations 0 uses the library default.

This plane has its own connection flags, because it speaks to the auth master socket rather than to an HTTP API:

FlagEnvironmentMeaning
--auth-addrYARILO_AUTH_ADDRmaster socket, default localhost:9102
--auth-certYARILO_AUTH_CERTmTLS client certificate
--auth-keyYARILO_AUTH_KEYmTLS client key
--auth-caYARILO_AUTH_CACA bundle signing the server certificate

With none of the TLS flags set the connection is plain TCP — development and smoke tests only.

sh
yarctl auth cache flush 'alice@*'
yarctl auth scram-verifier --password 'hunter2'

Effect: cache flush discards cached lookups; the next authentication goes to the userdb.

warden ​

yarctl backend warden dump [--output table|json]   # shorthand: yarctl warden dump

The connection accounting yarilo-warden holds: who is connected, from where, and how the per-user and per-IP limits stand against them. This is the state backend who is rendered from, shown raw.

Effect: none.

wait ​

yarctl wait [--timeout 2s] URL...

Probes every URL once, in order, and exits 0 when all answer: a 2xx for http://, an open connection for tcp://. The first failure prints not ready: … to stderr and exits 1. There is no retry loop: the caller repeats, as a Kubernetes startup probe does.

--timeout bounds each probe — DNS, dial and response — which is the difference from a shell read timeout that bounds only the read. A refused connection, an HTTP error status and a timeout are all reported the same way.

wait belongs to no plane, so it works in any pod regardless of YARILO_ADMIN_TYPE.

sh
yarctl wait --timeout 30s http://yarilo-auth:8080/readyz tcp://redis:6379

Effect: none; it only probes.


Output ​

-O human (the default) prints tables for the commands that have one: director status, backends, ring status, the short replies of the other director commands, quota, user and fts. Every other command prints pretty-printed JSON, and -O json forces JSON everywhere. who and warden dump have their own --output table|json.

Exit code 0 on success, 1 on error.