Commands Reference
Complete reference for every Aveloxis CLI command.
aveloxis serve
Starts the long-running scheduler that continuously collects repos from the priority queue, plus a web monitoring dashboard.
aveloxis serve [flags]
Flags
Flag |
Type |
Default |
Description |
|---|---|---|---|
|
string |
|
Address for the monitoring dashboard (the REST API is a separate process — see |
|
integer |
config |
Number of concurrent collection workers. Each worker claims one repo at a time from the queue. The cobra flag default is 1, but when the flag is not explicitly set, serve uses |
|
boolean |
|
Also load API keys from Augur’s |
Behavior
Uses the staged collection pipeline (API -> staging -> processing -> facade -> commit resolution -> analysis).
The queue is Postgres-backed (
aveloxis_ops.collection_queue) and usesSELECT ... FOR UPDATE SKIP LOCKEDfor atomic job claiming.Safe to stop and restart at any time. On shutdown (
Ctrl-C/SIGTERM), in-flight API calls and statements are cancelled at once; workers get up tocollection.shutdown_grace_seconds(default 10) to unwind (a completion reached at the cancel is stamped on a retry bounded by half the grace, at most 5 s), then queue locks are released and staging data is preserved.On startup, reclaims a previous serve’s locks at once (except a running heal’s parked rows), resumes collection, and drains any leftover staged data from an interrupted run in the background.
Stale locks from crashed instances are recovered after 1 hour by a running instance, or at once by the next serve start.
Multiple instances can share the same queue for horizontal scaling.
Periodic tasks
The scheduler also runs these background tasks:
Task |
Interval |
Description |
|---|---|---|
Org refresh |
Every 4 hours |
Re-fetches org membership lists |
Contributor breadth |
Every 15 minutes (config |
Discovers cross-repo contributor activity via GitHub Events API |
Materialized view rebuild |
Weekly (Saturday) |
Pauses collection, refreshes the 20 8Knot materialized views, resumes |
Supply-chain view refresh |
Every |
Refreshes the two Aveloxis-owned supply-chain views CONCURRENTLY; seconds, collection never pauses |
Stale lock recovery |
Every 5 minutes |
Re-queues jobs locked for more than 1 hour |
Scancode worker tuning
aveloxis serve also runs a decoupled ScancodeWorker pool (v0.21.0+) for per-file license + copyright scanning. The pool’s behavior is tuned via the collection block of aveloxis.json — no CLI flag. The most operationally relevant knobs:
|
Default |
What to change it for |
|---|---|---|
|
|
More concurrent scancode invocations (cap by CPU cores). |
|
|
Raise on RAM-rich hosts (e.g. |
|
|
Raise on fleets skewed toward big repos so the per-job timeout starts higher (default is adaptive — every wall-clock timeout doubles the next attempt’s budget up to |
|
|
Lower for testing or when source-license churn is unusually high. |
See configuration.md -> scancode worker for the full per-field reference and the v0.21.x → v0.25.x context.
Examples
# Start with defaults (collection.workers, default 12; dashboard on 127.0.0.1:5555)
aveloxis serve
# Start with 4 workers and a custom dashboard address (8082 is the web GUI's port)
aveloxis serve --workers 4 --monitor 127.0.0.1:5556
# Start using Augur's API keys
aveloxis serve --workers 4 --augur-keys
aveloxis web
Starts the web GUI for OAuth-based group management. Users log in via GitHub or GitLab, create groups, and add repositories or entire organizations for collection.
aveloxis web
Configuration
The web command has no CLI flags. All settings come from the web section of aveloxis.json:
Config field |
Type |
Default |
Description |
|---|---|---|---|
|
string |
|
Listen address for the web server. |
|
string |
|
External URL used to construct OAuth callback URLs. |
|
string |
|
Reserved; not read today (sessions are random in-process tokens). Set a random string anyway. |
|
bool |
|
Local HTTP development: cookies without |
|
string |
|
Where |
|
string |
|
The origin of a separate front end whose |
|
int |
|
Ordinary users’ repository batches up to this size are approved automatically; |
|
string |
|
GitHub OAuth app client ID. |
|
string |
|
GitHub OAuth app client secret. |
|
string |
|
GitLab OAuth app client ID (Application ID). |
|
string |
|
GitLab OAuth app client secret. |
|
string |
|
GitLab instance URL for self-hosted instances. |
OAuth app setup
GitHub: Create an OAuth app at https://github.com/settings/developers. Set the callback URL to
{web.base_url}/auth/github/callback.GitLab: Create an OAuth app at https://gitlab.com/-/profile/applications. Set the redirect URI to
{web.base_url}/auth/gitlab/callback. Check theread_userscope.
Behavior
Serves the web GUI on the configured listen address.
Users authenticate via OAuth, then create groups and add repos or orgs through the browser.
Breadcrumb navigation shows
Home / {Group Name}for easy navigation.Group detail pages display repos 25 per page with pagination controls. A case-insensitive search box filters by repo name, owner, or URL.
Repos added through the web GUI are inserted into the same collection queue used by
aveloxis add-repo.When a user adds a GitHub org or GitLab group, a
user_org_requestsrow is created. A background task scans tracked orgs every 4 hours to discover and queue new repos.Sessions are stored in-memory with a 24-hour expiry. Restarting the process clears all sessions.
Runs as a separate process from
aveloxis serve. Both share the database.
Examples
# Start the web GUI (uses settings from aveloxis.json)
aveloxis web
# Then open http://localhost:8082 in your browser
See the Web GUI guide for detailed setup instructions.
aveloxis api
Starts the REST API server on 127.0.0.1:8383 unless configured
otherwise. Serves repo statistics, weekly time series, dependency
licenses, scancode results, SBOM downloads, repo search, and the
Augur-compatible metric endpoints. The web GUI’s charts and the
comparison page load their data from this process.
aveloxis api # 127.0.0.1:8383, or api.addr from the config
aveloxis api --addr 0.0.0.0:9383 # override for this invocation
Flag |
Type |
Default |
Description |
|---|---|---|---|
|
string |
(from config) |
Listen address for the API server. Overrides |
The listen address is normally set with api.addr in aveloxis.json
(v0.28.19), because aveloxis start api spawns the process with only
--config — a flag-only setting could not reach a backgrounded API,
and two instances on one host collided on 8383. The default is
loopback on purpose; see
Reaching the API from another host
before binding a routable address, and remember that
web.api_internal_url has to follow the API when it moves.
Run alongside aveloxis serve and aveloxis web — aveloxis start all
starts all three. See the REST API guide for the endpoint
reference.
aveloxis scancode-worker
Runs ONLY the ScancodeWorker pool against the configured database (v0.27.6)
— the dedicated-scancode-host deployment, following the aveloxis api
single-purpose-process precedent. Scancode is the one subsystem whose
resource profile (multi-GB shallow clones, CPU-pinned Python subprocesses,
24-hour worst-case wall clocks) is nothing like the API-bound main pipeline;
moving it to an adjacent machine isolates its disk/CPU blast radius while
the shared tables (FOR UPDATE SKIP LOCKED claims + the
scancode_locked_host column) provide all the coordination needed.
aveloxis scancode-worker -c /etc/aveloxis/aveloxis.json
The primary server opts out by setting
"scancode_workers": 0(an EXPLICIT 0 — an absent key keeps the default of 2) in its ownaveloxis.json.No API keys required — scancode clones anonymously; this command starts without any
worker_oauthrows (unlikeaveloxis serve).Does NOT run schema migrations (the v0.21.5 contract: only
serveandmigratedo). It checks the schema version at startup and logs an ERROR pointing ataveloxis migratewhen the DB is behind.Writes its PID to
~/.aveloxis/aveloxis-scancode-worker.pid, and is managed like the other components:aveloxis start scancode-worker/aveloxis stop scancode-worker(v0.29.4). It is never part ofall(a primary that still runs its in-serve pool would double up).All
collection.scancode_*knobs apply (workers, cadence, clone dir, adaptive timeouts, ignore globs, timeout-cap strikes).Do not run
aveloxis serveon the dedicated host.serveis the full scheduler — it migrates and collects — whatever knobs the config carries. Since v0.29.4 the incident’s shape (a binary AHEAD of the schema stamp) is refused twice: thestart servedeploy gate refuses when the stamp is behind the binary (no migration of that binary has completed here — the ladder’s step 2: the release checklist’s migrate,aveloxis migrate --skip-viewson the standard ladder), and serve’s own startup migration refuses to run a full pass while anotheraveloxis-serveis connected. Since 2026-09-11 a same-versionstart serveis refused too: serve will not start a second full scheduler against a database that already has one, because the two compete for the same queue and API keys. If you genuinely want two, say so explicitly withaveloxis serve --allow-second-serve.stopscopes its backend check to this host.
Full recipe — Postgres remote access, minimal config template, systemd unit, libmagic version-lock — in the dedicated scancode host guide.
aveloxis test-mail
Sends a test email through the configured Gmail SMTP settings (v0.20.14).
Validates the mail block of aveloxis.json first (fail-fast on syntactic
mistakes like a bare domain in gmail_user or a non-App-Password), then
actually exercises the credentials.
aveloxis test-mail you@example.org
Use at deploy time so the first user signup isn’t the first SMTP attempt.
aveloxis collect
One-shot collection of specific repos without the scheduler. Uses the direct collection pipeline (bypasses staging, writes directly to relational tables). Best for testing or collecting a small number of repos.
aveloxis collect [flags] <url> [<url> ...]
Flags
Flag |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Full historical collection. Ignores the |
|
boolean |
|
Also load API keys from Augur’s |
Examples
# Incremental collection (only new data since last run)
aveloxis collect https://github.com/chaoss/augur
# Full historical collection
aveloxis collect --full https://github.com/chaoss/augur
# Multiple repos, mixed platforms
aveloxis collect \
https://github.com/torvalds/linux \
https://gitlab.com/fdroid/fdroidclient
aveloxis add-repo
Adds repositories to the collection queue. Platform is auto-detected from the URL.
aveloxis add-repo [flags] <url> [<url> ...]
Flags
Flag |
Type |
Default |
Description |
|---|---|---|---|
|
integer |
|
Queue priority. Lower numbers are collected first. |
|
boolean |
|
Import all repos from |
URL formats
# Single GitHub repo
aveloxis add-repo https://github.com/chaoss/augur
# Single GitLab repo (including nested subgroups)
aveloxis add-repo https://gitlab.com/group/subgroup/project
# GitHub organization (adds all repos in the org)
aveloxis add-repo https://github.com/chaoss
# Multiple repos at once
aveloxis add-repo \
https://github.com/torvalds/linux \
https://github.com/chaoss/grimoirelab
# High priority
aveloxis add-repo --priority 10 https://github.com/kubernetes/kubernetes
# Import from Augur
aveloxis add-repo --from-augur
Platform detection
URLs containing
github.comare treated as GitHubURLs containing
gitlabin the hostname are treated as GitLabHostnames listed in
gitlab.gitlab_hostsin the config are treated as GitLab
aveloxis add-key
Stores API keys in the database for use during collection.
aveloxis add-key [flags] [<token>]
Flags
Flag |
Type |
Default |
Description |
|---|---|---|---|
|
string |
(required unless |
Platform for the key: |
|
boolean |
|
Bulk import all keys from |
Examples
# Store a GitHub token
aveloxis add-key ghp_your_github_token --platform github
# Store a GitLab token
aveloxis add-key glpat-your_gitlab_token --platform gitlab
# Bulk import from Augur
aveloxis add-key --from-augur
aveloxis prioritize
Pushes a repository to the front of the collection queue.
aveloxis prioritize <url>
Sets the repo’s priority to 0 and due time to now. The scheduler will collect it next.
aveloxis prioritize https://github.com/chaoss/augur
Also available via the REST API:
curl -X POST http://localhost:5555/api/prioritize/42
Where 42 is the repo’s repo_id.
aveloxis recollect
Flags one or more repositories for a full (since=zero) re-collection on their next scheduler cycle.
aveloxis recollect <url>...
Sets the force_full_collect flag on each named repo’s aveloxis_ops.collection_queue row. When the scheduler dequeues a flagged repo, determineSince() returns zero time, triggering a full re-collection regardless of last_collected. The flag clears itself on the next successful completion.
aveloxis recollect https://github.com/chaoss/augur
aveloxis recollect https://github.com/a/b https://github.com/c/d # multiple
Use this command:
After a bugfix or schema change that invalidates previously-collected data for a specific repo.
To manually force a refresh when you suspect the incremental-since window missed something.
In combination with
aveloxis prioritize <url>to start the full re-collection immediately rather than at the repo’s normal due time.
Automatic triggering (v0.18.24+)
The scheduler also sets this flag automatically when a collection ends with an error that indicates incomplete PR child data — specifically the GraphQL PR batch error classes (stream CANCEL mid-body, “Timeout on validation of query”, or retry exhaustion). The next cycle then backfills whatever the failed batch missed. Operators see a force_full_recollect set WARN log line when auto-flagging fires.
See the troubleshooting guide for details on the error classes that trigger auto-flagging.
aveloxis migrate
Creates or updates the database schema. Without --skip-views it also drops
and re-creates every materialized view from its definition, which is the only
command that applies a changed view definition; with --skip-views the views
are left as they are.
aveloxis migrate
Creates 149 tables across three PostgreSQL schemas, plus 20 8Knot materialized views when collection.materialized_views is enabled (the default) and, always, the two supply-chain views:
aveloxis_data(103 tables + 22 materialized views) – all collected dataaveloxis_ops(42 tables) – operational stateaveloxis_scan(4 tables) – scancode per-file license/copyright results
Also performs a data cleanup pass that nullifies garbage timestamps (year < 1970) across all tables, preventing BC-era dates from poisoning queries.
Safe to run repeatedly. All DDL uses CREATE ... IF NOT EXISTS and inserts use ON CONFLICT DO NOTHING. Does not touch Augur schemas if sharing a database.
Upgrading an existing deployment across many releases? migrate covers every schema change and one-shot SQL backfill, but a separate set of operator-run heal commands (API-calling or long-running repairs) is deliberately not a migration — see Upgrading for the ordered list with the release that introduced each.
The completed-backfill ledger (v0.28.4)
Expensive one-shot data steps — keyset backfills, history rotations, dedups, the timestamp cleanup — record their completion in aveloxis_ops.migration_ledger and are skipped on every later migrate. Before the ledger, every version bump re-walked all of them as no-ops (~1.5–2.5 hours on a fleet-scale database). DDL steps (tables, columns, indexes, views) are deliberately not ledgered: an explicit aveloxis migrate still heals hand-dropped objects on every run.
A step is recorded only after it completes with zero errors — a failed step re-runs on the next migrate automatically. To force a specific step to re-run (for example after hand-repairing data it targets), delete its ledger row and migrate again:
-- See what has been recorded:
SELECT step_label, completed_at, tool_version FROM aveloxis_ops.migration_ledger ORDER BY completed_at;
-- Replay one step:
DELETE FROM aveloxis_ops.migration_ledger WHERE step_label = 'v0.27.104 backfill pull_requests.meta_head_id/meta_base_id';
then run aveloxis migrate --skip-views. Every ledgered step is idempotent, so replays are always safe.
aveloxis backfill-identities
One-off repair for missing identity attribution (v0.26.5 — see
summary/identity-attribution-audit-2026-07-09.md). The 2026-07-09
audit found cntrb_id 0% populated on issue_assignees,
pull_request_assignees, pull_request_reviewers, and
pull_request_meta, and issues.closed_by_id at 0.015% — while the
raw identity material (platform user ids) was stored at ~100%.
aveloxis backfill-identities --dry-run # candidate counts, no writes
aveloxis backfill-identities # all phases
aveloxis backfill-identities --phase 1 # assignment joins + pr_meta owners + pr_repo owners (SQL only)
aveloxis backfill-identities --phase 2 # closed_by from issue events (SQL only)
aveloxis backfill-identities --phase 3 # closed_by GraphQL timeline sweep (needs API keys)
Flags: --dry-run, --batch-size (primary-key window width per UPDATE batch, default
100000; on fleet-scale tables use 1000000), --limit (cap per phase, 0 = unbounded), --phase
(all|1|2|3), --sweep-batch (issues per GraphQL query in phase 3,
default 100).
Phases 1–2 are pure SQL derivation (no API calls; measured production
coverage 99.87–99.98% for assignments). Since v0.27.104, phase 1 also
runs the pull_request_repo.pr_cntrb_id owner pass — keyset windows
over pr_repo_id on a table that is ~41.2M rows on the production
fleet, so expect phase 1’s wall time to be dominated by it on large
installations (use --batch-size 1000000 there). Like the other
phases it is resumable and idempotent: interrupted runs re-walk
windows cheaply because filled rows (pr_cntrb_id IS NOT NULL) drop
out of the candidate set. Owner logins that match zero contributors
(deleted forks) or MORE THAN ONE active contributor (ambiguous —
v0.27.123) stay NULL; no fabricated attributions. Both owner passes
are GitHub-only — GitLab rows heal forward via ID-qualified owner
refs on re-collection. Phase 3 fetches closers the
history-capped events feed cannot reach, via per-issue
timelineItems(itemTypes:[CLOSED_EVENT]) batched ~100 issues per
GraphQL query (~3 points each). Run phase 2 after the v0.26.3
event-healing cohort completes for best coverage; all phases are
idempotent and resumable. Rows whose identity genuinely isn’t
derivable stay NULL — no data is fabricated.
aveloxis dedup-repos
Merges case-variant duplicate repositories (v0.25.32). GitHub and GitLab
treat owner/repo URLs case-insensitively, so
github.com/azure/azure-sdk-tools and github.com/Azure/azure-sdk-tools
are the same repository — but fleets that predate v0.25.32 could
accumulate both as separate rows, each collected in full (doubled API
budget, storage, and double-counted analytics).
aveloxis dedup-repos --dry-run # show the plan (first 20 pairs)
aveloxis dedup-repos --limit 50 # canary batch
aveloxis dedup-repos # full run — re-run until "0 pairs"
Flags
Flag |
Default |
Purpose |
|---|---|---|
|
false |
Print the merge plan without writing. Shows winner/loser URLs + repo_ids, per-side last-collected dates, and whether either side is mid-collection. |
|
50 |
Pairs merged per batch. Each pair is one heavy transaction (repoints + child-data deletes). |
|
0 |
Cap total pairs merged this run (0 = no cap). |
What each merge does
Per pair, winner = the oldest repo_id, in one transaction:
Repoints
user_reposto the winner — every user group that referenced either variant keeps the repo and immediately sees the winner’s collected data.Repoints shared-copy rows (
messages,email_message,commit_comment_ref,foundation_membership) — these tables are globally unique, so the pair shares one copy. (contributor_repois deliberately untouched: it is the breadth worker’s observational record of contributors’ GitHub-wide activity, keyed by the numericgh_repo_id, not a catalog reference.)Deletes the loser’s duplicated child data (issues, PRs, commits, releases, dependency scans, …) leaves-first, then the loser row itself. Nothing is lost: both sides collected the same repository.
Enqueues the winner if it has never been collected.
Pairs with either side status='collecting' are left out of each
batch’s window so they cannot stall the run (a mid-collection pair at
the head of the alphabetical order used to occupy a slot every round);
the end-of-run summary reports how many groups remain — re-run once
those jobs finish. The command is idempotent; merged pairs drop out of
the candidate set.
Precondition
The merge refuses to start until the v0.28.18 migrate has built the
email_message FK indexes (idx_email_message_repo_id /
idx_email_message_signaled_repo_id) — without them every pair would
sequential-scan that table on the repoints and again at commit. Run the
pending deploy steps first (aveloxis deploy-checklist --pending prints
them and names the migrate); the refusal names the
missing index. The precondition checks the indexes, not the schema stamp,
so a fleet that built them in an earlier release passes it on a binary
that is not yet deployed: keeping that order is up to you.
After the run
Once the pending deploy steps are done (so the views already carry the release’s definitions):
aveloxis migrate --skip-views # builds uq_repos_repo_git_ci — the permanent
# DB-level backstop (skipped with a WARN while
# duplicates remain)
aveloxis refresh-views # matviews stop double-counting immediately
Generic-git repos (platform 3) are never touched: unknown hosts may legitimately be case-sensitive, so their URLs stay byte-exact.
aveloxis migrate-cntrb-ids
One-shot opt-in data migration (v0.22.2): rewrites contributors whose
cntrb_id is a legacy random UUID to the deterministic
PlatformUUID(platform_id, platform_user_id) form, cascading through all
child tables via the v0.22.1 ON UPDATE CASCADE FKs.
aveloxis migrate-cntrb-ids --dry-run # show the plan
aveloxis migrate-cntrb-ids # execute (batched transactions)
aveloxis migrate-cntrb-ids --limit 10000 # incremental run
Flag |
Default |
Description |
|---|---|---|
|
|
Show the plan and a sample without writing. |
|
|
Rows per transaction (use 500 on fleet-scale DBs). |
|
|
Cap rows migrated this run (0 = no cap). |
|
|
DANGEROUS: skip the ON UPDATE CASCADE precheck. |
Run aveloxis refresh-views afterwards. Collision pairs are left for
merge-cntrb-collisions.
aveloxis merge-cntrb-collisions
Soft-merges the rename-collision pairs migrate-cntrb-ids skips (v0.22.3):
same platform user under two rows. Moves identities to the winner, merges
non-empty fields, inserts an alias, marks the loser cntrb_deleted = 1
(row stays in place — no FK rewrites).
aveloxis merge-cntrb-collisions --dry-run
aveloxis merge-cntrb-collisions
Flag |
Default |
Description |
|---|---|---|
|
|
Show the merge plan without writing. |
|
|
Pairs merged per batch (one transaction each). |
|
|
Cap total pairs merged this run. |
aveloxis heal-collection-gaps
aveloxis heal-collection-gaps --dry-run # list candidates + gap sizes
aveloxis heal-collection-gaps --repo-id 49290 # heal one repo
aveloxis heal-collection-gaps --workers 4 # the fleet pass
The isolated healer (v0.27.140) for issues/PRs lost to the pre-v0.27.139 incremental blind-window bug. Finds repos whose metadata issue/PR counts exceed the stored counts and runs a threshold-0 gap fill on each: list every issue/PR number from the API, diff against the database, fetch ONLY the missing items (with children and comments), stage, process.
Targeted by construction — only count-gap candidates are visited
(~5% of a typical fleet), never a 100% rescan. A healed repository keeps
its place in the recollection cycle (v0.29.68: the heal’s release leaves
due_at alone; before, every healed repository became due at once), and a
serve started while a heal runs leaves the heal’s parked repositories to
it (its heartbeat keeps them fresh; only a heal whose heartbeat stopped for
longer than the stale-lock window is reclaimed). The candidate query is
the resume state: healed repos drop out, so the workflow is re-running
until “0 candidates”. Safe beside a running serve: each repo is
drain-locked for the duration of its heal, and repos mid-collection
are skipped (a rerun catches them). Neither the lock nor the heal
touches last_collected. While a repo is locked the monitors count it
under Parked (drain / heal). Ctrl-C (or SIGTERM) stops the run
cleanly, releases every repo it still holds, and exits nonzero
(v0.29.65). The log line names the after_repo_id of the interrupted
page’s start, so --after-repo-id revisits that page; a plain rerun
works too.
Flags: --dry-run (list the candidates and their gap sizes, healing
nothing; with --repo-id, print that repo and whether a real run could
lock it — before v0.29.65 that combination actually healed the repo),
--limit N, --workers N (default 4),
--repo-id N, --after-repo-id N (keyset resume), --all (sweep
every collected repo — the completeness mode for repos whose
stored-but-deleted rows numerically hide the gap; not recommended for
routine use). Exits nonzero when any repo’s heal failed, and with
--repo-id also when the repo could not be locked: the message says
whether it is being collected, is parked by a staging drain or another
heal run, has no queue row (gone or dequeued, so nothing will heal it),
became queued again in the meantime (rerun now), or whether its queue
status could not be read.
Sizing, measured on a ~140K-repo fleet (2026-08-23): 6,809 candidates /
279,100 items took ~65 hours at --workers 4 (the largest single repo
filled 24,845 items); reruns then converged in minutes. Expect a small
residual candidate set that heals with filled=0 on every rerun — repos
whose forge metadata count exceeds what the forge’s own listing returns
(transferred or hidden items); that is the floor, not unfinished work.
Run it on a binary at v0.27.139 or later — earlier binaries re-open
the blind window on the next routine cycle. Typical ordering after an
upgrade across the v0.27.13x train: aveloxis migrate first, then
this command, then aveloxis refresh-views once the heal settles.
aveloxis refresh-views
Manually refreshes the materialized views this database has — both sets, or one.
aveloxis refresh-views # both sets (--set all)
aveloxis refresh-views --set 8knot # the 20 views in matviews.sql (8Knot / analytics)
aveloxis refresh-views --set supply-chain # explorer_package_exposure + explorer_package_advisory (seconds)
aveloxis refresh-views --aggregates # + the dm_repo_* / dm_repo_group_* aggregate tables
Uses REFRESH MATERIALIZED VIEW CONCURRENTLY where unique indexes exist, so reads are not blocked during the refresh. A set the database does not have is reported and skipped. aveloxis serve refreshes the 8Knot set weekly (default Saturday; collection.matview_rebuild_day, which can also disable it) and the supply-chain pair every collection.supply_chain_refresh_hours (default 24; 0 disables the schedule). A refresh keeps each view’s definition: a release that changes an 8Knot view needs a plain aveloxis migrate, which re-creates that set; every migrate re-creates the supply-chain pair.
--aggregates (v0.28.18) additionally runs the dm_ aggregate pass after the views — the same per-repo loop the weekly rebuild runs unless collection.matview_rebuild_skip_dm_aggregates is set. It is off by default because that pass runs for hours to days at fleet scale; with the skip knob on, this flag is the only way the dm_ tables update. The pass holds a database advisory lock for its whole duration — if the weekly scheduler rebuild (or another --aggregates run) is already in it, the command exits nonzero with another dm_ aggregate rebuild is already running instead of interleaving two DELETE+INSERT passes over tables that have no unique key.
Both halves keep going past a failing view or repo but exit nonzero when any failed (v0.28.18): the error names the first ten failures with the full count, the rest of the pass’s work stands, and a view failure does not skip the aggregate pass. Scripts wrapping the command will see exits they never saw before — that is the point; re-run after fixing the named view or repo.
aveloxis sbom
Generates a CycloneDX or SPDX Software Bill of Materials from the dependency data collected for a repository.
aveloxis sbom 42 --format cyclonedx -o sbom.json
Flag |
Default |
Description |
|---|---|---|
|
|
Output format: |
|
stdout |
Write to a file instead of stdout. |
|
|
Also store the SBOM in |
The repo must have been collected with dependency analysis (automatic
under aveloxis serve). SBOMs are also downloadable from the web GUI and
the REST API.
aveloxis install-tools
Installs optional analysis tools.
aveloxis install-tools
Installs three external analysis tools: scc (per-file code complexity; requires Go), scorecard (OpenSSF security posture; release tarball), and scancode (per-file license/copyright scanning; requires Python 3.10+ and pipx — also injects the typecode-libmagic plugin). If the Python prerequisite is missing, scancode installation fails and per-file license scanning never runs; see aveloxis upgrade-tools for updating installed tools.
If scc is not installed, the code complexity phase is silently skipped during collection.
Exits non-zero if any tool fails to install or installs off PATH (the same LookPath serve’s phases use), so aveloxis install-tools && aveloxis start all stops before a start without the tool (v0.29.68). Both install-tools and upgrade-tools are non-interactive: each tool runs in its own process group with pip and git prompts disabled (PIP_NO_INPUT, GIT_TERMINAL_PROMPT=0; a Homebrew formula install never prompts), so credentials must come from configuration or a keyring and a pip or git credential prompt fails at once (any other prompt, such as an ssh host-key or passphrase question when git fetches over ssh, is not answered and ends at the bound); each tool is bounded by the same limit as the monthly update check, and Ctrl-C kills the tool in flight and ends the walk. upgrade-tools and the monthly update check count a scorecard upgrade as failed when an older scorecard earlier on PATH would still be the one serve runs, and name both paths (v0.29.69).
aveloxis upgrade-tools
Upgrades the external analysis tools in place (v0.23.6). Unlike
install-tools (which short-circuits when a tool exists), this re-runs
the install path to pull updates: go install ...@latest for scc, a fresh
release tarball for scorecard, and pipx upgrade for scancode (preserving
venv customizations) plus a typecode-libmagic re-injection.
aveloxis upgrade-tools
Exits non-zero if any tool fails to upgrade, so it can run from a maintenance cron.
aveloxis start
Launches aveloxis components as detached background processes with log output redirected to files in ~/.aveloxis/.
aveloxis start serve # scheduler + monitor → ~/.aveloxis/aveloxis.log
aveloxis start web # web GUI → ~/.aveloxis/web.log
aveloxis start api # REST API → ~/.aveloxis/api.log
aveloxis start scancode-worker # dedicated scancode worker → ~/.aveloxis/scancode-worker.log
aveloxis start all # serve + web + api (never the scancode worker)
PID files are written to ~/.aveloxis/aveloxis-{serve,web,api,scancode-worker}.pid. If a component is already running, the command reports it and skips the launch. scancode-worker (v0.29.4) is the dedicated-host process from Dedicated Scancode Host; all deliberately excludes it.
The exit status is the contract for scripts: a component that could not
be started — a pidfile that cannot be read (the command refuses rather
than risk a second scheduler on the host), a log file that cannot be
opened, a failed exec — makes the command exit nonzero, naming each
failure, after every requested component has been attempted. start all
therefore still tries web and api beside a refused serve, and says so.
Since v0.29.68 start writes the child’s pidfile at once (the
“already running” guard reads that file, so a second start during the
child’s startup is refused), then waits for the child’s readiness signal
on an inherited pipe and reports a child that exited first. web and api
signal readiness once the schema gate has passed AND the port is bound (a
port in use is a refusal the process exits on); the scancode worker once
the schema gate has passed; serve once its keys are loaded and its startup
migration is done. web, api and the scancode worker refuse to start while
the schema stamp is behind their binary, so a --skip-deploy-check start
on an un-migrated fleet reports them as exited; start them again after the
migrate. A child still starting after 30 seconds (a long migration for
serve) is reported as started. An already-running component is a no-op
and exits 0.
Log files are opened in append mode — existing content is preserved across restarts.
Note: start does NOT survive a reboot. For production hosts that should
come back automatically after a restart, run the three processes under
systemd instead — see Running Aveloxis as a Service.
Pick one manager per host: once the systemd units own the processes,
aveloxis start/stop must not be used there.
aveloxis stop
Gracefully stops background aveloxis processes.
aveloxis stop serve # stop only the scheduler
aveloxis stop web # stop only the web GUI
aveloxis stop api # stop only the REST API
aveloxis stop scancode-worker # stop the dedicated scancode worker
aveloxis stop all # stop serve + web + api (never the scancode worker)
aveloxis stop # (no args) same as 'all'
Sends SIGTERM to the specified component(s) using PID files in ~/.aveloxis/. For serve: in-flight API calls and statements are cancelled at once; workers get up to collection.shutdown_grace_seconds (default 10) to unwind (a completion reached at the cancel is stamped on a retry bounded by half the grace, at most 5 s), then queue locks are released; staging data is preserved. For web and api: the listener closes, in-flight requests get 10 s to finish, then the pool closes. The scancode worker has its own bounds (collection.scancode_shutdown_grace_minutes; see Graceful shutdown). PID files are removed after a successful stop or when they are stale (process no longer running); a file the command could not read, whose process it could not signal, or that it could not remove (the message carries the error) is left in place for you to inspect; a stale file another start replaced meanwhile is left to that start. stop all names a scancode worker it left running.
Nothing to stop is exit 0 — stop is idempotent. A process that was
found but could not be signaled (typically operation not permitted on a
process another user started) is a failure: its pidfile is left in place,
every other requested component is still stopped, and the command exits
nonzero naming the one it could not. A pidfile that exists but cannot be
read — unreadable, or carrying something that is not a PID — is the third
arm: it is not evidence that the component is stopped, so the command
still tries the pgrep fallback — which normally finds a component that
start launched, so you will see Stopped serve (PID N) AND a nonzero
exit reading serve stopped, but: pidfile left in place …. The file is
neither stale nor live, so it is left for you to inspect and delete by
hand; the next start refuses on it until you do. If pgrep finds
nothing either, the exit is still nonzero and reads serve: pidfile left in place … — nothing was stopped, and nothing could be confirmed. A
pgrep that itself fails (not installed, or any exit other than its
documented “no match”) is likewise not evidence of absence: the command
exits nonzero reading serve: pgrep for serve: ….
After SIGTERM the command watches pg_stat_activity for the component’s
backends and, past the shutdown budget, prints the persistent PIDs with a
pg_terminate_backend recipe. Since v0.29.4 that recipe is offered for
this host’s backends only — the ones whose client address is this
session’s own, or local to the database host (a unix socket, or loopback
in either address family). Every other backend carrying the same tag
falls into one of two reported arms, is never waited on, and is never
offered for termination:
Another client address — normally another machine running this component against the same database (the primary’s serve seen from a dedicated scancode host, or the reverse). The check knows the address, not the machine, so a serve on THIS host that dialed a different DSN address reads the same way.
Address not visible — a backend of a database role whose privileges this one does not hold.
pg_stat_activityshows a session’sclient_addronly to roles that HOLD that session’s role’s privileges and to roles that holdpg_read_all_stats’s; everyone else reads NULL, the same NULL a unix socket shows.stopprints a no-verdict note for these — neither this host’s nor another’s.
To get a verdict on the second arm, run stop as that role, or grant
this one the stats privileges:
GRANT pg_read_all_stats TO <role>;
Holding is the test, not membership: a NOINHERIT role, or one granted
WITH INHERIT FALSE, is a member of pg_read_all_stats with none of its
privileges and still reads NULL. On PostgreSQL 16 and later a grant’s
inherit option is fixed at GRANT time, so if the grant already exists and
does not inherit, re-grant it with
GRANT pg_read_all_stats TO <role> WITH INHERIT TRUE. Check with
SELECT pg_has_role(current_user, 'pg_read_all_stats', 'USAGE').
The check needs the config (-c) to reach the database; without it the
command says the check was skipped.
How a backend is placed on a host
Each component tags its connection pool with an application_name that
carries the component and this machine’s hostname, separated by @:
aveloxis-serve@kate
aveloxis-scancode-worker@runner-01
aveloxis-web@kate
aveloxis-api@kate
stop matches on the part before the @ (the component) and
compares the part after it (the host) against its own machine.
The rule in one sentence: a marker can only separate hosts, never merge them. The client address decides, and the marker can only veto it. A backend is placed on this host when its client address is this host’s and its marker does not contradict that — so a backend that reaches the database from a different address is never this host’s, whatever its marker says, and two backends sharing an address but carrying different markers are correctly separated. Either marker being absent means the marker abstains and the address rule alone decides.
The composition is deliberately one-directional: it never places a
backend here that the client-address rule alone would have placed
elsewhere. That is what keeps marker quality out of the safety
argument — os.Hostname() is not unique across machines (two
container hosts running the same compose file report the same
hostname), so a marker collision degrades to the address rule rather
than to a pg_terminate_backend recipe for a machine you are not on.
Your own pg_stat_activity queries should match with LIKE 'aveloxis-%' (which still works unchanged) or with
split_part(application_name, '@', 1) = 'aveloxis-serve'. Matching the
full tag by equality will find only the backends of one specific host.
A host whose kernel will not report a hostname tags un-suffixed
(aveloxis-serve) and falls back to the client-address rule.
Warning
Behind a transaction pooler (pgbouncer in transaction or statement
mode, pgcat, Odyssey), the client address is useless — the host marker
is not. Every client reaches PostgreSQL through the pooler, so
pg_stat_activity.client_addr is the pooler’s address for every
backend, this host’s and every other host’s alike. The address rule
alone would read them all as one host and offer a terminate recipe for
another machine’s serve. The @host marker survives the collapse
because PostgreSQL stores the application_name the client sent and
never rewrites it.
The residual is the upgrade window. A backend tagged by a
pre-v0.29.4-round-12 binary carries no marker, so it falls back to the
address rule — and behind a pooler that rule reads it as this host’s.
Until every host on the database runs a build with the marker, treat a
“Persistent PIDs (this host)” entry on a pooled deployment as
unverified and confirm with ps on each host before terminating. Once
every component is upgraded, the verdict holds through a pooler.
The other residual: one host reached over two addresses reads as
two. If a component was started against a DSN naming this machine’s
LAN address while stop runs against a localhost DSN (or one side
goes through a pooler and the other does not), the addresses differ,
the address rule says “another host”, and the marker cannot overrule
it. stop then reports the component’s own backends under “other
hosts” and returns without waiting out their drain. From inside the
database that case is indistinguishable from two machines that happen
to share a hostname, and only one of the two readings can print a
terminate recipe for a machine you are not on — so aveloxis takes the
safe one. Run stop with the same -c config the component was
started with and the addresses match.
A session-mode pooler is unaffected in principle only if it preserves
the client address, which pgbouncer does not — but with markers in place
that no longer matters for the host verdict.
Note
aveloxis stop also works for processes started in the foreground (e.g., aveloxis serve), because all foreground processes write PID files on startup.
aveloxis data-test
Operator-driven shadow-database verification harness for schema
changes. Builds binaries from a tagged release and the local working
tree, provisions two scratch databases (aveloxis_released and
aveloxis_new), collects the same repo into each, and reports
row-count differences. Catches data-loss regressions before they
ship. Shipped in v0.22.8.
See the full guide: Schema-change verification.
Flags
--released-tag TAG— git tag of the released aveloxis version to compare against (e.g.,0.22.6). The tag must exist in the local clone;git fetch --tagsif missing. Required.--repo URL— git URL of the test repo to collect into both scratch DBs.augurlabs/auguris the canonical choice. Required.--keep-dbs— retain the scratch DBs after the run. Default is to drop them. Pass when you want to inspect a failing table viapsqlafter the report is written.--work-dir PATH— where to put binaries, logs, and the report. Default is/tmp/aveloxis-data-test-<UTC-timestamp>.
Behavior
Builds the released binary via
git worktree add(reuses local clone’s objects — no remote fetch).Builds the local binary from the current working tree.
Connects to the configured PostgreSQL host using the operator’s
aveloxis.jsoncredentials. The user must have CREATEDB privilege because the harness creates and drops scratch databases.Copies API keys from the operator’s primary
aveloxis_ops.api_keystable into both scratch DBs — operator doesn’t re-paste tokens.Collections run sequentially (~30 min each), not parallel, because both sides share the API key pool.
Exit code 0 on PASS or FLAG-only; exit code 1 on any FAIL (row loss detected) — suitable for CI gating.
Examples
# Validate the current working tree against v0.22.6 using augur
aveloxis data-test --released-tag 0.22.6 \
--repo https://github.com/augurlabs/augur
# Keep scratch DBs for ad-hoc inspection after a FAIL
aveloxis data-test --released-tag 0.22.6 \
--repo https://github.com/augurlabs/augur \
--keep-dbs
# Custom work directory (useful for CI artifact retention)
aveloxis data-test --released-tag 0.22.6 \
--repo https://github.com/augurlabs/augur \
--work-dir /var/cache/aveloxis-data-test
The full report is written to <work-dir>/report.md. See
Schema-change verification for guidance on
interpreting PASS / FLAG / FAIL results.
Column-fill diff (v0.26.1)
Row counts can’t see a column the new binary stopped populating (the
canonical case: issue_labels.platform_label_id is 0 on every row
under the GraphQL path while row counts match exactly). After the
row-count diff, data-test therefore also compares per-column FILL
COUNTS — how many rows carry a meaningful value, type-aware (<> ''
for text, <> 0 for numerics, IS NOT NULL otherwise) — across every
column of every base table in aveloxis_data, aveloxis_ops and
aveloxis_scan. The fourth schema, aveloxis_augur_data, holds only
the Augur-compatibility views — derived data with no base tables to
diff — so it is deliberately out of scope.
FAIL (exit code 1): a column populated under the released binary is completely unpopulated under the new one — a dropped mapping or renamed JSON tag.
FLAG: fill counts differ — review; small drift is expected since the two collections run against a live repo minutes apart.
aveloxis shadow-diff
Semantic equivalence diff of issue/PR collection between two databases (the harness behind the REST → GraphQL refactor’s per-phase validation). Compares issues, PRs, labels, assignees, reviewers, reviews, commits, files, messages, and bridge tables by platform-stable keys.
aveloxis shadow-diff --rest-dsn postgres://.../aveloxis_shadow_rest \
--graphql-dsn postgres://.../aveloxis_shadow_graphql
Flag |
Default |
Description |
|---|---|---|
|
(required) |
Postgres DSN for the baseline database. |
|
(required) |
Postgres DSN for the comparison database. |
|
|
Restrict the diff to one repo_id (0 = all). |
|
|
Machine-readable report for CI pipes. |
Exit code 1 on any FAIL-level difference. For a coarser whole-schema
row-count comparison, see aveloxis data-test.
aveloxis reconcile-repos
Heals the stranded-repo class: non-archived repos rows with no
collection_queue row — invisible to the scheduler forever. Production
audit (2026-07-21) root-caused the cohort to GitHub renames + prelim’s
duplicate skip+dequeue (which dequeues without archiving), plus a
smaller lost-enqueue share. Each stranded repo is classified by a LIVE
redirect check:
dead upstream (404/410/451) → archived (the outcome prelim applies)
redirects to a tracked repo → dataless duplicates heal onto the winner (
HealRenamedDuplicate); data-bearing duplicates consolidate through the dedup-repos per-pair machinery (repoints + leaves-first deletes)redirects to an untracked URL → re-enqueued (prelim renames it in place on the next cycle — rename detection is prelim’s job)
alive at its own URL → re-enqueued (a lost queue row)
aveloxis reconcile-repos --dry-run # per-repo classification, no writes
aveloxis reconcile-repos --limit 50 # canary
aveloxis reconcile-repos # everything
The scheduler also logs a startup gauge (non-archived repos with no collection_queue row) pointing here whenever the count is non-zero.
Re-run until stranded = 0; healed repos drop out of the set. Since
v0.29.68 Ctrl-C or a SIGTERM (kill <pid>) cancels the in-flight probe
or statement, ends the walk and prints an interruption summary (what was
archived, healed, consolidated and enqueued so far, with the dry-run marker
when set); the exit is nonzero, every outcome is idempotent, and a rerun
walks the whole cohort again (there is no resume marker).
Both consolidation arms (the dataless heal and the per-pair merge)
share dedup-repos’ precondition: the v0.28.18 migrate must have built
the email_message FK indexes, or each such repo is skipped with a
warning naming the migrate to run first (the other classifications —
dead, re-enqueue — proceed) and the run exits nonzero at the end so a
script cannot read a refused run as success. The same nonzero exit
applies when a stranded repository’s stored URL carries credentials
(v0.29.57): the row is not probed, is logged as an error and counted as
refused; correct repo_git and re-run.
aveloxis generate-showcase
Renders the PUBLIC collection showcase pages (v0.27.77 growth plan):
every admin-curated collection becomes a static, SEO-indexable HTML
page under <out>/showcase/, plus a showcase index, plus the site
sitemap.xml (when --gui-root is given — the generator is the
single writer of the sitemap once deployed). Pages carry full
meta/OG/JSON-LD and render ONLY collection metadata + cached per-repo
counts — never user names, stars, or group ownership (queries run
anonymously). Writes are atomic (tmp+rename) and pages whose
collection was deleted or renamed are pruned.
Since v0.27.78 the collection tables order by collected issues
(commit counts are bot-floodable — automation repos with millions of
machine commits headlined the tables), and the top 5 non-fork
repositories of each collection also get a public repo snapshot
page under <out>/repos/, deduped across collections. Forks
(repos.forked_from non-empty, captured by Phase 0 since v0.27.78)
are skipped and the next non-fork slides into the freed slot. Each
snapshot carries the cached commit/issue/PR counts, description and
primary language, a static weekly-activity chart in the signed-in
grammar (commits + issues + PRs-opened stacked bars, PRs-merged line
overlay; trailing 12 months, baked SVG — no JS, no endpoints), every
temporal CHAOSS-metric chart the signed-in repo page shows (the
seven store-backed metrics plus burstiness, project velocity, and the
drive-by/repeat retention cohorts — computed through the SAME
functions the compare API uses), each line chart carrying the live
site’s full trend grammar (dashed OLS trend, ±2σ residual tube, red
breach dots, slope/R² chip — a Go port of lib/trend.js with
identical formulas), the latest OpenSSF Scorecard table,
and the open-vulnerability posture — with honest “not yet scanned” /
“analysis pending” / “no collected activity” states instead of
fabricated zeros. Collection pages highlight the featured rows and
carry a sign-in reminder as its own row directly under them;
everything below the cut stays forge-linked. Snapshot pages ride the
same sitemap, prune pass, and privacy contract.
v0.27.80 also emits /showcase/compare.html — a fully static
comparison demo of the four featured repos with the most comparable
activity levels, one baked SVG chart per headline metric (commits,
issues, change requests, contributors), linked from the showcase
index. The slug compare is reserved (a collection literally named
“Compare” gets compare-2).
aveloxis generate-showcase \
--out /var/www/aveloxis-gui/showcase \
--gui-root /var/www/aveloxis-gui \
--base-url https://aveloxis.io
The whole run is bounded by --timeout (default 60m; accepts any
Go duration like 90m or 2h; 0 or negative disables the deadline
entirely). The pre-v0.27.88 hardcoded 10-minute budget was sized
before the snapshot pages carried the full chart set — on a fleet
whose featured repos include kubernetes-scale projects, the ~16
snapshot pages × ~10 analytics queries each exceed 10 minutes and the
run died with a misleading per-query context deadline exceeded on
whichever chart the clock expired at (aborting compare.html, the
sitemap, and the prune pass with it). If a run still times out, raise
--timeout — the first run on a cold Postgres cache is the slowest;
subsequent hourly runs benefit from warm buffers.
Runs hourly from a systemd timer (the Aveloxis project’s own site keeps
its unit with the site). Read-only on the schema; does not run migrations
(v0.21.5 policy). Safe alongside an active aveloxis serve.
aveloxis adopt-forge-id
Treats a repository that was deleted and re-created on its forge under the same URL as a continuation of the one Aveloxis already tracks (v0.29.62).
aveloxis adopt-forge-id --list # recorded changes: pending, superseded, adopted
aveloxis adopt-forge-id --repo-id 126257 --repo-id 98226
aveloxis adopt-forge-id --repo-id 126257 --note "upstream re-created by the org"
A re-created repository gets a new numeric forge ID. The org scan notices
the stored and the forge’s ID differ, logs a forge-ID mismatch ERROR, and
records the change as pending; it never changes the repository row itself.
--repo-id asks GitHub or GitLab for the repository’s current ID and
creation date, replaces the stored ID, and records the change as adopted.
It refuses, changing nothing, when the forge lookup fails, when the forge
still reports the stored ID, or when the stored ID changed in the meantime.
A GitLab repository is only looked up on the configured GitLab instance
(project IDs are per instance, and the keys stay on that host); a generic
git repository has no API to ask.
The repository page then shows: “Note: this repository changed forge
identifiers on
An admin can do the same from the web GUI (v0.29.63): the approvals page
lists pending changes under “Re-created repositories”, each with an Adopt
button, one per recorded change. The button adopts exactly that change, with
the creation date the scan saw; it does not ask the forge again, because the api
process holds no forge keys. The card is hidden when nothing is pending.
When the scan recorded two changes for one repository and one is adopted,
the other no longer starts from the stored ID: --list marks it
superseded, it leaves the card, and adopting it is refused.
aveloxis backfill-repo-metadata
Operator-driven fleet sweep (v0.27.79) that refreshes
repos.repo_description, primary_language, languages,
repo_archived, and forked_from from the forge APIs — one
FetchRepoInfo per GitHub/GitLab repo, written through the same
UpdateRepoMetadata path the collector’s Phase 0 uses. Generic-git
repos are skipped (no API).
Built as a launch accelerator for fork status: v0.27.78 added fork capture to Phase 0, but the per-cycle forward path takes ~6–21 days to cover the fleet. This command front-loads it — roughly an hour for a 94K-repo fleet at the default 8 workers, costing ~94K GraphQL points (about a quarter of one hour of a 73-key pool’s budget).
aveloxis backfill-repo-metadata --dry-run # candidate count only
aveloxis backfill-repo-metadata --limit 100 # canary
aveloxis backfill-repo-metadata # full sweep
aveloxis backfill-repo-metadata --after-repo-id 51234 # resume
Idempotent (every write is the forge’s current truth) and resumable —
progress lines and the summary print the last repo_id processed;
re-run with --after-repo-id after an interruption. Repos that 404
(renamed/deleted/private) are skipped and left to prelim’s normal
rename/dead-repo handling. Does not run migrations (v0.21.5 policy).
aveloxis rewalk-whitespace
One-time full-history whitespace bootstrap (v0.27.105 — the fill-audit
Workstream C). commits.cmt_whitespace was never measured while
SUM(cmt_whitespace) feeds six live aggregate queries; the facade now
measures it per cycle with Augur-parity semantics (an added blank line
counts as whitespace, not added; a whitespace-only reformat of a
8-character line counts as whitespace instead of an add/remove pair — so
cmt_added/cmt_removedon walked rows become the ADJUSTED Augur numbers). The per-cycle phase is incremental past a stamped marker (repos.whitespace_head_hash); this command does the full-history walk per repo, reusing the facade’s persistent bare clones, and stamps the marker so every later cycle stays incremental.
aveloxis rewalk-whitespace --limit 5 # canary first
aveloxis rewalk-whitespace --workers 8 # fleet run
aveloxis rewalk-whitespace --repo-id 12345 # single repo
aveloxis rewalk-whitespace --after-repo-id 51234 # keyset skip-ahead
Runtime expectation (grounded 2026-08-19): git log -p measured
~209 MB/s single-threaded; the fleet’s patch volume estimates at
~5–10 TB (162.7M commits, ~146B changed lines). At 8 workers expect
roughly 8–24 hours wall-clock (outer bound ~2 days if disk-bound);
the per-repo progress lines and every-50-repos running totals let you
extrapolate from the first hour. Safe alongside a running serve —
repos mid-collection are skipped this pass, and overlapping updates
are same-value idempotent. Resumable by construction: the marker IS
the resume state, so a re-run skips every already-walked repo. After
the fleet run, aveloxis refresh-views --aggregates (or the next
scheduled aggregate rebuild) picks the corrected sums into the dm_
tables.
Does not run migrations (v0.21.5 policy) — run aveloxis migrate
first so the marker column exists.
aveloxis data-verify
The standing data-verification program (v0.27.43): runs the read-only invariant probe battery against the configured database and exits 1 on any FAIL — suitable for gating releases and cron runs. Probes: structural invariants (case-duplicate repos, Default-group singleton, stranded repos, stale >24h locks, cross-kind message corruption with migration-state awareness), sampled count-integrity (cached queue counts vs actual rows, gathered vs forge metadata at the gap detector’s 5% line, batch-vs-single stats agreement), and fill rates (report-only unless floors are set).
aveloxis data-verify # full battery, human report
aveloxis data-verify --json # automation form
aveloxis data-verify --sample 500 # wider drift/equality sample
aveloxis data-verify --ground-truth 10 # ALSO re-fetch live GitHub
# metadata for 10 sampled repos
# and compare (needs API keys)
aveloxis data-verify --min-identity-fill 95 # optional floors → WARN below
Severities: FAIL = a code-enforced invariant is broken (exit 1); WARN = known-and-healing state or operator-attention signal (pending heal worklist, stranded repos, schema behind the binary — each WARN names its fix command); OK = verified. Safe against production: every probe is read-only and bounded.
aveloxis heal-messages
Repairs message rows corrupted by the pre-v0.27.38 cross-kind ID
collision (issue/PR conversation comments, inline review comments, and
review bodies share overlapping GitHub ID sequences; under the old
two-column arbiter the later writer silently overwrote the earlier
kind’s text — 198,237 rows on the production fleet). The v0.27.38
migration captures the affected rows into
aveloxis_ops.message_heal_worklist; this command consumes it.
Each pass refetches the claiming parents’ comments per-item
(parent-deduplicated — API cost is per distinct issue/PR, not per
collision), re-creates the correct rows under the kinded arbiter,
deletes the stale cross-kind bridge links, and stamps healed_at.
Failed parents leave their rows pending; re-run until “nothing
pending”.
Since v0.28.1 the command loops internally in 25,000-row passes and
stamps healed_at at the end of EACH pass, so an interrupted run
(Ctrl-C, reboot, kill) keeps everything already healed — at most one
pass of progress attribution is lost. Hand-chunking with --limit
is no longer load-bearing; --limit caps the rows CONSIDERED this
run (claimed rows + parent refetches — a failure-heavy canary stays
a canary).
Since v0.28.8 passes walk the worklist by a strictly increasing msg_id cursor: a fully-failing batch no longer stops the run (or starves the rows behind it) — the run continues past it, failed rows stay pending, and a re-run retries them from the bottom. The command exits nonzero whenever any claimed row failed to heal, so cron/scripts notice incomplete runs.
aveloxis heal-messages --dry-run # plan: pending rows, distinct parents
aveloxis heal-messages --limit 1000 # canary pass
aveloxis heal-messages # everything pending
Flags: --limit (0 = all pending), --dry-run, --augur-keys.
Requires GitHub API keys (this healer refetches). Safe with serve
stopped; after a fleet restart, run it once the force-full wave
crests so it doesn’t compete with collection for the key pool.
aveloxis heal-libyear
One-shot healer for libyear values that could never be computed.
A dependency’s libyear is the age gap between the version in use and the
latest release, so it needs both release dates. When either is missing the
calculation returns 0 — which is indistinguishable from a dependency that
really is on the latest release, and avg() counts a 0 while it skips a
NULL. Measured on a 3.6M-row fleet in September 2026, 87% of every row
reading libyear = 0 actually meant “unknown”, and the reported fleet
average was less than half the honest figure.
Since v0.29.57 the collector stores NULL for those rows as each repository is re-analysed. This command corrects the rows already stored that re-analysis can never fix: a dependency declared with no pinned version has no release date to measure from, whatever the registry says later.
aveloxis heal-libyear # dry run: report how many rows qualify
aveloxis heal-libyear --apply # replace those numbers with NULL
aveloxis refresh-views # make explorer_libyear_summary reflect it
The dry run is the default — unlike reconcile-repos, where --dry-run
is the opt-in flag. This rewrites hundreds of thousands of rows of collected
data, so the mutation is the flag and the preview is free.
Rows whose version IS pinned but whose release date was missing are deliberately left alone: v0.29.56 fixed the Go and Maven resolvers responsible for most of them, so re-analysis fills in real dates. NULLing them here would erase rows the next collection cycle is about to answer properly.
Walks keyset windows over the primary key, so it is safe alongside a running
serve and a cancelled run simply resumes on the next invocation.
aveloxis heal-commit-daily
One-shot filler for aveloxis_data.repo_commit_daily, the table the
repository page’s weekly commit series and top-contributors commit counts
read (0.29.73).
The commits table holds one row per file per commit, and one repository’s
rows lie scattered about one per page across the whole table, so the two
commit answers for a large repository read millions of pages (a kernel fork:
two minutes, past the front end’s budget). The facade now folds the walk it
already makes of the whole default branch into one row per repository, UTC
day and author email, and replaces the repository’s rows after every
completed walk; the two readers sum that table when the repository’s
picture is complete (repos.commit_daily_complete_at, set by a facade walk
whose every commit was proven written and by this command — never by a
walk that swallowed writes; 0.29.75) and the window is UTC-day aligned,
and read the commits table otherwise. Author
identity: the commits table’s stored id when this command carried it and
its contributor is still live (not merged away; 0.29.81), else
GitHub’s numeric user id a noreply address carries (it survives a rename),
else the login it names (through LOWER(gh_login)), else the house email
rule (the contributor’s own emails, then contributors_aliases), each only
when unambiguous among live contributors.
A repository collected before 0.29.73 has no rows until its next collection, and one whose only walk since swallowed commit writes has rows but no stamp; both are listed. This command fills such repositories now, largest first, by one scan of each repository’s commit rows — the cost its first page view pays today, paid once here:
aveloxis heal-commit-daily # dry run: how many repositories need it
aveloxis heal-commit-daily --apply # fill them, largest first
aveloxis heal-commit-daily --apply --limit 200 # only the 200 largest
Each repository is its own transaction, so an interrupt loses at most the
one in flight; rerun to finish. It can run beside a running serve: every
repository’s own next collection fills it anyway, so this only brings the
largest forward — and a repository whose collection finishes between this
command’s listing and its turn is skipped, because that walk’s fold is the
authoritative default-branch picture (a rebuild from the commits table
would bring back force-pushed commits); the closing line counts them. Minutes per kernel fork, seconds for most repositories; on a
large fleet run it under nohup and read its log.
aveloxis heal-vulnerabilities
One-shot healer that re-scans every repository with stored vulnerability rows. Two healing classes it covers:
Rows stored before v0.27.4’s two-phase OSV fetch (they carry
severity=UNKNOWNand empty summaries — the querybatch endpoint only returns id stubs). The re-scan fills severity, CVSS, summary, fix versions, aliases, and references from/v1/vulns/{id}.False positives from malformed dependency versions (v0.27.71 — the “6.0.3, fixed in 5.4” class: an unparseable version defeats OSV’s version matching and every advisory in the package’s history is reported). As of v0.27.72 the scan normalizes stored versions and rebuilds purls at read time, so this command heals those findings immediately after deploy — no need to wait for each repo’s analysis phase to rewrite its dependency rows.
aveloxis heal-vulnerabilities # all repos with findings
aveloxis heal-vulnerabilities --limit 50 # bounded canary run
Idempotent (prefer-nonempty upserts never downgrade filled data) and
safe alongside a running serve. Without this command the same
healing happens gradually as each repo’s next scheduled collection
cycle re-scans it.
aveloxis mark-gone-repos
Probes repositories that have no collection-queue row against their forge and maintains the “gone” state (v0.28.1). Background: when a tracked repository is deleted — or its whole organization goes private, as the US Department of Veterans Affairs did in mid-2026 — the URL returns 404 and prelim sidelines it permanently (archives + removes from the queue). All collected data is kept, but before v0.28.1 nothing distinguished that state from “never collected”, so the GUI showed a false “queued for first collection” banner over real data.
The probe is bidirectional and only DEFINITIVE answers decide:
404/410/451 → stamp
repos.repo_gone_at. The repo page then shows “This repository is no longer publicly available or traceable on GitHub” over the data we hold.200 on a previously-stamped repo → clear the stamp and re-enqueue: if the organization re-publicizes, collection resumes automatically on the next run of this command.
Anything else (transport errors, rate limits, 5xx, unresolvable redirects) → the repo is skipped and a re-run retries it.
A stored URL carrying credentials (
https://user:token@…, a row written before v0.29.57 refused such URLs) → not probed, logged as an error naming the repository, counted asrefused, and the command exits nonzero: a re-run cannot help untilrepo_gitis corrected. Scripts wrapping the command will see an exit they never saw before.
aveloxis mark-gone-repos --dry-run # list what each probe would do
aveloxis mark-gone-repos # stamp / clear / re-enqueue
aveloxis mark-gone-repos --limit 100 # bounded canary
Idempotent and re-runnable on any cadence. Since v0.29.68 Ctrl-C or a
SIGTERM (kill <pid>; aveloxis stop knows only the four long-running
components) cancels the in-flight probe or statement, ends the walk and
prints an interruption summary (what was probed, stamped, cleared and
skipped so far); every stamp is its own statement, so what landed stays,
and a rerun walks the whole cohort again — every verdict is idempotent,
so nothing is lost, only time. Dataless stranded rows
(no queue row, no data, no archived flag) are not candidates — there
is nothing to display for them either way. New gone repos are
stamped automatically by prelim at collection time; this command
exists for the historical cohort and for resurrection checks.
Since v0.29.7 aveloxis serve re-checks gone repositories on its
own. A 404/410/451 removes the repository’s queue row, and the scheduler
only ever visits queued repositories, so prelim never probes it again;
before v0.29.7 a repository made private and later public again stayed
“gone” until this command ran. The scheduler’s recheck ticker now
re-probes every gone-stamped repository once per
collection.gone_repo_recheck_days (default 28, up to 500 per hour,
never-checked rows first) with the same probe and the same verdict
rule, and logs gone recheck: repository is reachable again when it
resurrects one. This command remains the immediate, whole-cohort form:
run it after an upgrade or when you know an organization has flipped
back, and it resets the cadence clock on every repository it verifies.
The probe is one unauthenticated HEAD per candidate and consumes no
API-key budget: at roughly 200 ms per probe a few thousand candidates
take minutes and tens of thousands take an hour or more of wall-clock,
and nothing else; --limit bounds a first run. See Recurring
maintenance.
aveloxis run-scorecard
Bulk remote-primary OpenSSF Scorecard pass (v0.27.5). Walks every
non-archived, at-least-once-collected GitHub repo and runs
scorecard in remote mode (--repo, the full ~18-check set including
Code-Review, Maintained, Contributors, Branch-Protection, CI-Tests,
CII-Best-Practices, Signed-Releases), oldest-scorecard-first — repos
that have never been scanned come first. Exists because the per-cycle
collection phase historically ran local mode (~11 checks); this
command upgrades the fleet’s scorecard data without waiting for each
repo’s recollect cadence.
aveloxis run-scorecard # the whole backlog
aveloxis run-scorecard --limit 50 # canary run
aveloxis run-scorecard --older-than 180 # only stale/never-scanned repos
aveloxis run-scorecard --workers 8 # default NumCPU/2, capped at NumCPU
Details:
Refuses to start while
aveloxis serveis running on this host (checked via the serve pidfile + process liveness) — the pass would compete with the collection workers for the shared GitHub API budget. Stop serve first:aveloxis stop serve. It also writes its own pidfile (~/.aveloxis/aveloxis-run-scorecard.pid) so two bulk passes can’t overlap.Uses the SAME invoke/persist code as the collection phase: the comma-separated multi-token
GITHUB_TOKENbuilt from the key pool (collection.scorecard_token_count), the per-attempt wall-clock timeout (collection.scorecard_timeout_minutes, default 15), and thescorecard_modemarker on every stored row.The tokens are borrowed from the pool for each repo, not once for the whole pass (v0.29.10).
No analysis clone exists in this pass, so there is no local backstop: a failed remote attempt is logged, counted, and skipped — the repo’s next collection cycle retries. If no GitHub token can be lent (a misconfigured empty key), the repo is skipped at once and counted as failed rather than run without a token (v0.29.10): scorecard’s remote mode without a token waits up to an hour for GitHub’s rate limit, longer than the default 15-minute timeout.
Progress prints every 100 repos: done/total, cumulative
api_calls_used, elapsed, and ETA.api_calls_usedis a sum of one-token samples (instrument_token_sample): each run’s/rate_limitused-delta on the first lent token. It misses calls scorecard makes on the other lent tokens and counts other callers’ calls on the same token, so read it as an order of magnitude, not an exact count. Runs whose sample is unknown (a probe failed, or the rate-limit window reset mid-run) are excluded and counted asunknown. Interrupting with Ctrl-C is safe; the oldest-first ordering makes a re-run resume where it left off.Requires GitHub API keys (
aveloxis add-key). Does not run schema migrations (v0.21.5 contract) — runaveloxis migratefirst when upgrading to v0.27.5 so thescorecard_modecolumn exists.
aveloxis staging-stats
Read-only view of the JSONB staging table, per repo and entity type (v0.22.4): row counts, processed/unprocessed split, oldest/newest timestamps, and approximate bytes.
aveloxis staging-stats # top 10 (repo, entity_type) cohorts
aveloxis staging-stats --top 50
aveloxis staging-stats --repo microsoft/vscode
Use it to confirm the staging_retention_hours cleanup is working and to
spot repos whose staged rows aren’t being processed.
aveloxis distribution-stats
Read-only rollup of the distribution-tracking subsystem (v0.24.0): which repos publish to package registries, and which declare packaging manifests without any registry evidence.
aveloxis distribution-stats # fleet rollup
aveloxis distribution-stats --orphans # manifest-without-registry list
aveloxis distribution-stats --repo owner/repo # per-repo drill-down
Requires collection.distribution_tracking_enabled: true. See the
distribution architecture doc.
Mailing-list commands
These commands register and verify the mailing-list ingestion subsystem
(off by default; set collection.mailing_list_enabled = true to collect).
See Mailing-list ingestion for how the
subsystem works.
aveloxis load-foundation-core-repos
Loads one core/primary repository per project across the tracked open-source
foundations (Apache TLPs + podlings). Idempotently registers each project’s
flagship repo into a foundation group. Renamed from import-foundations
(kept as a hidden alias so existing scripts keep working).
aveloxis load-foundation-core-repos
aveloxis load-foundation-core-repos --dry-run
aveloxis load-foundation-orgs
Registers each foundation’s GitHub org(s) as tracked orgs under your
user, so the periodic org-refresh ticker continuously discovers new repos.
Tracking the apache org pulls all ~3,000 apache/* repos (a large
collection-budget commitment — surfaced in the output) so sibling repos
like arrow-rs get collected and mailing-list repo signals resolve.
aveloxis load-foundation-orgs --dry-run
aveloxis load-foundation-orgs --yes
Flag |
Description |
|---|---|
|
owning user (default 1) |
|
print planned registrations without writing |
|
proceed without the interactive confirmation |
aveloxis load-apache-lists
For each Apache PMC, ensures a per-PMC repo_group, links the PMC’s primary
repo, and registers the dev@ and users@ lists for collection. Run
load-foundation-core-repos first so the primary repos exist; PMCs whose
repo isn’t in the catalog are skipped.
aveloxis load-apache-lists --dry-run
aveloxis load-apache-lists
aveloxis register-mailing-list
Registers a single mailing list for collection under any archive system —
used for curated, non-catalog lists like the kernel’s lore public-inbox.
The list is attached to the repo’s repo_group (named after the repo, so
multiple lists for one repo share a group).
aveloxis register-mailing-list \
--system lore_public_inbox \
--list linux-pci@vger.kernel.org \
--repo https://github.com/torvalds/linux
Flag |
Description |
|---|---|
|
archive system ( |
|
list address |
|
repo URL to attach the list’s discussion to (must already exist in the catalog) |
aveloxis backfill-issue-external-keys
Populates issues.external_key from bracketed [KEY-N] title prefixes
(Apache Jira → GitHub issue imports). This is what lets issue_event
mailing-list mail bridge to the imported issue by its Jira key.
aveloxis backfill-issue-external-keys
aveloxis mailing-list-stats
Read-only coverage rollup: registered lists, email_message counts, mirror
rate, signaled-repo resolution, sender-identity resolution, and the
per-class distribution. Safe to run alongside an active serve.
aveloxis mailing-list-stats
The same data is available over HTTP at GET /api/v1/mailing-list/stats
(see REST API).
aveloxis verify-mailing-list
The Phase 4 branch-coverage harness. Reads the collected data and prints a
PASS / EMPTY / DEFER table for every logic branch (each msg_class, both
backends, each routing outcome, threading, signaled/sender resolution,
external_key backfill), plus the contributor-resolution assessment.
aveloxis verify-mailing-list # report only (exit 0)
aveloxis verify-mailing-list --strict # exit non-zero if a required branch is empty
Flag |
Description |
|---|---|
|
exit non-zero when a required (mailing-list-native) branch produced zero rows |
--strict gates only the mailing-list-native branches. Cross-subsystem
branches (bridged-to-issue/PR, mirror-linked, sender-resolved,
external_key) report as DEFER and never gate — they fill in
steady-state operation once the linked repos’ GitHub data is collected and
the periodic backfills run. See
Mailing-list ingestion §12 for the
collection-ordering caveat.
aveloxis deploy-checklist
Prints the current release’s manual deploy/heal steps (read-only). Releases that heal data a plain restart does not touch — v0.29.0 is one — register a checklist here.
aveloxis deploy-checklist
aveloxis deploy-checklist --pending # every step THIS database still needs — the range the start gate enforces — and the migrate to run
aveloxis deploy-checklist --since 0.29.64 # every release's steps after the last acknowledged deploy, oldest first
aveloxis deploy-checklist --since 0.29.64 --inclusive # the same, 0.29.64's own steps included
--pending reads the database’s last acknowledged deploy and schema stamp
and prints the range aveloxis start serve enforces, computed by the same
code: after the acknowledged release; from the stamp, inclusive, when
nothing was acknowledged or the stamp is behind the acknowledgement; the
binary’s own steps when neither can be used. It prints nothing pending
only when the schema stamp is readable and current and this release’s steps
are acknowledged (or it has none); a missing, unreadable or malformed stamp
prints the steps, with a first line saying why, and so does a database
with no collected data yet (the start gate does not require the steps on a
fresh install). Otherwise its last line is
the migrate to run. It does not take --since or --inclusive. Use it for
upgrades (Upgrading).
--since is the offline form: it takes a version you name — typically the
last acknowledged deploy, the highest version in aveloxis_ops.deploy_ack
compared as a version — and prints every later release’s block up to this
binary, oldest first, without reading the database. It is exclusive: that release’s steps already
ran. When no deploy was ever acknowledged, pass the schema stamp (SELECT schema_version FROM aveloxis_ops.schema_meta, read before the migrate
moves it) with --inclusive, which prints the stamp release’s own block as
well: its steps were never acknowledged either (PR #218 fix review r1).
--inclusive without --since is an error. Consecutive releases with identical steps print as one block
labelled with the range it covers. When more than one release has steps, a
header comes first: run aveloxis stop all, the migrate and aveloxis start all once, not once per block — the migrate is the strongest any block
asks for (a plain aveloxis migrate if any block needs one and no later
release in the range lifts it, as 0.29.61 lifts 0.29.60’s; otherwise
aveloxis migrate --skip-views) — then every block’s checks, heals and
audits, oldest first, then aveloxis ack-deploy, then aveloxis start all,
and a step that needs the running release (such as adopt-forge-id or the
approvals page) after the start. The start gate’s refusal names the same
migrate for the range it prints. A --since value
that is not a version (dotted non-negative numbers such as 0.29.64) is an
error: the command names the flag and exits non-zero.
aveloxis start serve prints the same accumulated list, with the same
header and collapsing, when the last acknowledged deploy is behind the
binary: the heals of a skipped release never ran (v0.29.68).
Upgrading puts both commands in order.
aveloxis ack-deploy
Records that the current binary version’s deploy/heal steps were run,
so aveloxis start serve / aveloxis start all stops prompting for
them. Run it AFTER completing the steps deploy-checklist --pending prints. It cannot
stand in for step 2: while the schema stamp is behind the binary the
start gate refuses regardless of the acknowledgement (v0.29.4) — only a
completed migration of that binary (the checklist’s migrate step —
aveloxis migrate --skip-views on the standard ladder, a plain aveloxis migrate for v0.29.57) moves the stamp.
aveloxis ack-deploy [--note "..."]
The start gate: on an EXISTING fleet, aveloxis start serve and
aveloxis start all refuse to start (or, in an interactive terminal,
prompt) when the current release has un-acknowledged deploy steps —
the release’s data-side healing must not be silently skipped. Since
v0.29.4 the gate also reads the schema stamp, on EVERY release and
BEFORE the ledger: a stamp behind the binary proves no migration of
that binary has completed here (the ladder’s step 2), so it refuses
without a prompt and records nothing — an acknowledgement cannot clear
it, only a completed migration of that binary (the checklist’s migrate
step, which the refusal names — aveloxis migrate --skip-views on the
standard ladder) can. Fresh installs, releases without deploy
steps on a current stamp, and acknowledged releases whose stamp is
current, start silently. When another aveloxis-serve is already
connected to the database the gate prints a note (never a refusal)
with the other serve’s client addresses, each carrying the code’s own
verdict: an “(other address)” entry is normally the primary, so this
host is running the wrong command (aveloxis start scancode-worker is
the alternative); a “(this host)” entry is a serve on THIS host,
either one still running or a backend of one just stopped here still
draining (only aveloxis start refuses to double-start a component).
The two labels report the host verdict, not a comparison of the
address text alone: since v0.29.4 the verdict also consults the
@host marker, so behind a pooler a backend can be tagged “(other
address)” beside an address string identical to your own — the marker
is what separated them.
When NO “(other address)” entry appears — every listed entry is
“(this host)”, or an address this database role cannot see — the note
withdraws the wrong-command reading, because nothing in the listing
identifies the primary: on a primary restarting beside its own
draining backends that verdict would be exactly backwards. Automation
can bypass with --skip-deploy-check, which prints the evidence, then
the release’s deploy steps, and proceeds (serve’s own startup
migration still refuses beside another serve).
aveloxis register-jira-projects
Seeds aveloxis_ops.jira_project_serve from the synthetic Jira issues
the mailing-list projection already minted — one registration per
distinct project key (191 on the production aveloxis DB), each
mapped to its repo. Idempotent; re-running never clobbers an
operator-fixed repo mapping. Makes no network calls: dead upstream
keys are disabled by the collector on their first 400.
aveloxis register-jira-projects --dry-run
aveloxis register-jira-projects
Flag |
Default |
Description |
|---|---|---|
|
off |
List derivable projects without registering. |
|
|
Jira Server base URL for the registrations. |
aveloxis backfill-jira-identities
The one-shot identity + state pass over every registered Jira
project: bulk-searches each registration’s Jira Server (identity and
state fields; comments are the collector’s job) and writes reporter
and assignee identity + authoritative issue state onto the synthetic issues
through the same provider-precedence writers the collector uses. The
registrations in jira_project_serve are the ONLY project source —
their repo mapping and base_url are operator-correctable, and the
command refuses to run before aveloxis register-jira-projects has
created them. Assignee identities are banked into jira_identities
alongside reporters (the username is the perishable half). A
resolve/mint failure skips that issue and the command exits nonzero —
rerun to retry; all writes are idempotent. The full-history pass uses
the same drift-safe walk as the incremental worker (window cursor +
fixed ceiling — never bare offsets over a mutable ordering), and
carries NO resume marker: --limit is a canary only, and a rerun
restarts from the beginning (harmless — every write is idempotent).
Measured cost: ~2–3 polite hours for the full 844,401-issue ASF
corpus at 1,000 issues per search call.
Run it soon regardless of whether the collector is enabled: the stable Server-era username this matches on (49.2% of issues unambiguous by login, +17.6% by display name) does not exist in Jira Cloud’s API — if the ASF instance migrates to Atlassian Cloud, the whole corpus’s usernames become unfetchable at once.
Identity policy (SR-6): unambiguous matches link to existing
contributors; pure Jira-only identities (zero candidates) mint a
contributor so networks include the person; ambiguous identities
stay recorded in jira_identities with a NULL link.
aveloxis register-jira-projects # once, first
aveloxis backfill-jira-identities --dry-run
aveloxis backfill-jira-identities --project KAFKA --limit 2000 # canary
aveloxis backfill-jira-identities # the full pass
Flag |
Default |
Description |
|---|---|---|
|
off |
Report per-project issue totals without writing. |
|
|
Restrict to one project key. |
|
|
Stop after N issues (canary). |
aveloxis strip-quoted-history
Fills messages.msg_text_clean — the quote-stripped body — on
mailing-list messages ingested before the ingest-time stripper
existed. Measured corpus facts that motivate it: 82.5% of Apache list
mail embeds the thread it replies to (64% of body characters are
quotation), so raw email bodies average ~4,800 characters against
GitHub comments’ ~274; stripping brings the email median to ~300.
Consumers read COALESCE(msg_text_clean, msg_text) — until this walker
completes, unstripped historical rows serve their raw text.
Resumable: msg_text_clean IS NULL is the resume state, so rerunning
after an interruption skips completed rows automatically. After a
pattern-library version bump, run with --rule-rerun to re-strip rows
stamped under the older rule.
aveloxis strip-quoted-history --limit 50000 # canary
aveloxis strip-quoted-history # full history (~30-45 min on a 12.6M-body fleet)
Flag |
Default |
Description |
|---|---|---|
|
|
Stop after N rows (canary); 0 = run to completion. |
|
off |
Re-strip rows stamped under an older rule version. |
|
|
Rows per read+stamp batch. |
aveloxis resolve-email-identities
Resolves mailing-list sender identities in one fast pass: every
retained sender_email on an unattributed mailing-list message body is
re-joined against the current contributors / contributors_aliases
tables (email, canonical, and alias chains — the same chain commit
resolution uses), walking msg_id keyset windows from floor to
ceiling. A full pass over a 12.6M-body fleet measures ~5–10 minutes.
The serve-side backfill ticker
(collection.mailing_list_sender_backfill_interval_minutes, default
hourly) does the same walk continuously; this command is for
“converge NOW” — after aveloxis migrate, after
backfill-identities, or on a freshly healed fleet. Safe beside a
running serve (row-scoped UPDATEs on cntrb_id IS NULL rows, with
deadlock retry). Rerun until it reports 0: unresolved rows stay
pending, and an interrupted run resumes with --after-msg-id from the
printed cursor.
aveloxis resolve-email-identities --dry-run # count resolvable rows
aveloxis resolve-email-identities # the full pass
Flag |
Default |
Description |
|---|---|---|
|
off |
Count currently-resolvable rows and exit without writing. |
|
|
Resume cursor from an interrupted run. |
|
|
|
aveloxis backfill-mailing-list-projection
Projects already-ingested mailing-list issue_event mail onto issues
(mailing-list Phase 5). In-place and idempotent; batched.
aveloxis backfill-mailing-list-projection --batch 500
Flag |
Default |
Description |
|---|---|---|
|
|
Rows per batch. |
aveloxis load-numfocus-projects
Loads the embedded NumFocus project catalog (v0.25.4): creates the “NumFocus Sponsored” / “NumFocus Affiliated” user groups and adds each project’s flagship repository.
aveloxis load-numfocus-projects --dry-run
aveloxis load-numfocus-projects
aveloxis load-numfocus-projects --detect-new # report catalog drift vs numfocus.org
Flag |
Default |
Description |
|---|---|---|
|
|
Owner of the created groups. |
|
|
Preview without writing. |
|
|
Crawl numfocus.org and report projects missing from the embedded catalog. |
|
(embedded) |
Override the embedded YAML catalog. |
aveloxis load-numfocus-orgs
Companion to load-numfocus-projects: registers each NumFocus project’s
GitHub organization for ongoing repo-discovery refresh (“NumFocus
Sponsored Orgs” / “NumFocus Affiliated Orgs” groups). Same flag surface as
load-numfocus-projects.
aveloxis load-numfocus-orgs --dry-run
aveloxis load-numfocus-orgs
aveloxis version
Prints the Aveloxis version.
aveloxis version
Global behavior
Config file
Every command reads the file named by -c/--config, default aveloxis.json in the current working directory. A missing file is not an error: the command logs config file not found, using defaults at WARN and runs on compiled defaults (the database augur as user augur on localhost:5432, every other key at its default) — so a migrate or add-key run from the wrong directory acts on that default database. Pass -c /path/to/aveloxis.json on every command in scripts, units and runbooks; an invalid file (bad JSON, a value refused at load) is fatal. version and install-tools read no configuration.
Exit codes
Code |
Meaning |
|---|---|
0 |
Success |
1 |
General error (invalid arguments, config not found, database connection failed) |
Signal handling
aveloxis serve handles the following signals:
SIGTERM/SIGINT(Ctrl-C) – graceful shutdown. In-flight API calls and statements are cancelled at once; workers get up tocollection.shutdown_grace_secondsto unwind (a completion reached at the cancel is stamped on a retry bounded by half the grace, at most 5 s), then locks are released; staging data is preserved.SIGTERMsent byaveloxis stop– same graceful shutdown.