Monorepo for the TypeScript projects that power Chaotic-AUR. It contains the backend API, the frontend website and a small shared library.
- backend/ β NestJS (Fastify) API: repository management, package/build data, router & download metrics, GitLab integration, authentication
- frontend/ β Angular (OptimusUI) website
- shared-lib/ β shared constants and TypeScript types used by both
- Repository management: watches Arch Linux (core/extra) for updates, computes rebuild triggers and bumps dependent Chaotic-AUR packages
- ELF signal scanner: scans
.pkg.tar.zstarchives withbsdtar,readelfandnmto index sonames, imported/exported symbols and directory ownership. This drives dependency detection and catches plugin breaks (e.g. kwin) that a plain soname diff would miss. - Package, router and download-metrics API
- Chaotic-AUR website with charts, build logs, MR overview and mirror map
- GitLab integration for merge request reviews and pipelines
- Nx monorepo management
- NestJS on Fastify
- Angular with OptimusUI and TailwindCSS
- TypeORM + PostgreSQL
- Redis (Chaotic Manager / Moleculer microservice)
- Moleculer for build events
- TypeScript, Vitest
- Node.js 26+ and
pnpm11+ - A PostgreSQL database
A local development setup can be spun up using Docker Compose from the repo root:
docker compose up -dInstall dependencies:
pnpm installStart the backend and frontend (in two terminals):
pnpm start:be
pnpm start:fe- Backend runs at
http://localhost:3000, Swagger docs athttp://localhost:3000/api - Frontend runs at
http://localhost:4201
pnpm build # build all projects
pnpm test # run all tests
pnpm test:e2e # run backend tests
pnpm test:coverage:combined # run all tests and generate a combined coverage report
pnpm lint # run all linters (eslint)
pnpm format # format with prettierYou can also target a single project, e.g. pnpm exec nx test backend.
See backend/src/config/ for the full list. The most important ones:
| Variable | Default | Description |
|---|---|---|
PG_HOST / PG_PORT |
localhost / 5432 |
PostgreSQL connection |
PG_USER / PG_PASSWORD / PG_DATABASE |
chaotic |
PostgreSQL credentials |
NODE_ENV |
development |
production disables TypeORM schema sync |
CAUR_PORT |
3000 |
Backend listen port |
CAUR_DB_KEY |
β | AES key used to encrypt repo API tokens at rest |
REDIS_PASSWORD / REDIS_SSH_* |
β | Redis + SSH tunnel for Moleculer events |
REPOMANAGER_SCHEDULE |
0 * * * * |
Cron schedule for the repo-manager run |
REPOMANAGER_MIRROR_URL |
https://arch.mirror.constant.com |
Arch mirror used for the .files DBs and package archives |
REPOMANAGER_MIRROR_POLL |
0 * * * * * |
Cron schedule for mirror lastupdate polling |
REPOMANAGER_SIGNAL_SCAN_ENABLED |
false |
Enable the ELF signal scanner |
REPOMANAGER_ABI_DRY_RUN |
true |
Log plugin-ABI bumps instead of rebuilding |
VIRUSTOTAL_API_KEY |
β | Enables VirusTotal checks of MR sources and download URLs |
VIRUSTOTAL_REQUEST_SPACING_MS |
15000 |
Pause between VirusTotal requests (free tier: 4/min) |
VIRUSTOTAL_POLL_INTERVAL_MS |
20000 |
Pause between polls of a running VirusTotal URL analysis |
The repo manager (backend/src/repo-manager/) watches the Arch mirror:
- A cron job pulls the
core/extra.filesdatabases on the configuredREPOMANAGER_SCHEDULE. - It diffs package versions against the stored
archlinux_packagerows to find changed packages, then clones the Chaotic-AUR repos and computes rebuild triggers (explicit, global, dependency or plugin-ABI based). - When
REPOMANAGER_SIGNAL_SCAN_ENABLED=true, changed Arch packages are downloaded and scanned for ELF signals before triggers are computed.
A second cron job polls the mirror's lastupdate file so repo re-syncs are
noticed near-instantly (REPOMANAGER_MIRROR_POLL).
The scanner (backend/src/repo-manager/signal.ts + signal-scan.service.ts)
downloads a package archive and, per shipped ELF object (shared objects and
executables):
bsdtar -tfβ file list (used for directory-ownership / plugin detection)bsdtar -tvfβ executable bit per file (to also readDT_NEEDEDof binaries)readelf -dβDT_NEEDED+SONAMEnm -D --undefined-onlyβ imported symbolsnm -D --defined-onlyβ exported symbols (shared objects only)
Results are stored in package_elf_analysis (keyed by pkgType, pkgId,
version); the directory-ownership index used for plugin detection is derived
from it in memory on demand.
Each analysis also carries a broken flag + brokenReasons. A package is
flagged broken (the static equivalent of checkrebuild's ldd "not found" scan)
when it:
- links a soname (
DT_NEEDED) that neither the global provided-soname index nor the base system provides β a dependency was dropped or its soname renamed, or - ships files under a stale versioned runtime directory β e.g. files in
usr/lib/python3.12/...after the repo'spythonmoved to 3.13 (same for perl/ruby/ghc).
The provided-soname index is built from all stored analyses, so a full mirror index must be seeded first for accurate results. Missing-soname detection is skipped until the index is populated to avoid false positives on a fresh DB.
When REPOMANAGER_SIGNAL_SCAN_ENABLED=true, a package that newly becomes
broken after an Arch update is rebuilt automatically (a BROKEN_DEPS bump).
Only breaks introduced by the current run count, so long-standing issues don't
cause rebuild loops. Like the plugin-ABI channel, this respects
REPOMANAGER_ABI_DRY_RUN (log-only unless set to false).
The schema is managed with TypeORM migrations. They run automatically at
startup (migrationsRun: true in backend/src/data/data.source.ts), so a fresh
database is created on the first boot without manual steps.
We follow the Contributor Covenant. Before submitting a PR:
pnpm lintandpnpm testmust pass- New migrations must be registered in
data.source.ts - Commit messages should follow Conventional Commits (the repo ships a commitizen + commitlint pre-commit hook)