Raise a job against a door, cost it as the work happens, close it. Then settle move-out damage with an ingoing/outgoing inspection comparison instead of an argument.
For managing agents, landlords with a portfolio, body corporates and facilities teams. One static binary, one SQLite file. No cloud account, no subscription, no external service — and it keeps accepting work with no connection at all.
Quick start · Screenshots · How it works · Status · Docs
Pango — Swahili for the lease, and for the den it is held on.
Part of Vulos — rooted in vula, the Zulu and Xhosa word for open.
Maintenance and inspection software for people who look after property — managing agents, landlords with a portfolio, body corporates, facilities teams.
It runs as one binary and one SQLite file. A tablet, a laptop, an office NAS or a Raspberry Pi is a complete deployment. Copy the file, run it, done.
Two things it does that general ticketing tools do not:
Costs are per door, not per ticket. Every job belongs to a unit, and spend
and labour aggregate per building and per unit. Units are real records with a
normalised key, so Flat 3A, 3A and flat 3a are one door rather than three
rows quietly fragmenting your reporting.
Inspections compare. Run an ingoing condition report at move-in and an outgoing one at move-out, and Pango diffs them item by item — what changed, in which direction, and what degraded. Damage liability becomes evidence rather than a disagreement.
Eleven surfaces, each in light and dark — the full set is in docs/SCREENSHOTS.md. All captured from the compiled binary in demo mode by npm run screenshots; nothing is mocked up, and the script refuses to write a placeholder if the binary does not serve the app.
git clone https://github.com/vul-os/pango && cd pango
npm install && npm run build # build the app
bash scripts/build-embedded.sh # compile it into the binary
./backend/pango --demo # http://localhost:8099Demo mode runs entirely in memory with a seeded portfolio — no database, no
configuration, nothing written to disk. Sign in as demo@pango.local /
demopassword.
For a real deployment:
./backend/pango --db /var/lib/pango/pango.db --addr 0.0.0.0:8099The first account you register becomes the owner; registration then closes, and further accounts are created by an authenticated operator. See SELFHOST.md.
flowchart LR
subgraph "One binary"
API["Go API<br/><i>chi</i>"]
DB[("SQLite<br/><i>+ oplog</i>")]
UI["React app"]
SITE["Docs & landing"]
end
P2["Another site<br/><i>office · contractor</i>"]
USB["Shared folder<br/><i>NAS · USB stick</i>"]
UI --> API --> DB
API <-->|"signed ops<br/>Ed25519"| P2
API <-->|"ops-<node>.jsonl"| USB
The building is the authority. Whoever manages a building owns its jobs, its job numbering and its inspections. Because the only contended decision — who does the work — has exactly one legitimate writer, there is no consensus protocol, no leader election and no distributed lock anywhere in the system.
Money and hours are append-only. A job's cost is SUM over its ledger at
read time, never a stored column. Two people costing the same job while
offline therefore add rather than overwrite, and a correction is a negative
entry, so the audit trail is complete by construction.
Peers are enrolled by hand. No discovery, no rendezvous, no hub. An
operator enters another node's URL; requests are mutually signed with Ed25519.
For sites with no connectivity, each node appends only its own
ops-<node>.jsonl to a shared folder — so a NAS or a USB stick is a valid
transport with no possibility of a write conflict.
Honest per-area accounting. A feature that silently does nothing is worse than one that says it is not built.
| Area | State |
|---|---|
| Jobs — raise, triage, assign, cost, close | Built |
| Buildings and units, unit normalisation | Built |
| Append-only cost and time ledgers | Built |
| Reports — per building, per unit | Built |
| Inspections — condition capture, completion | Built |
| Ingoing/outgoing comparison | Built |
| Auth, org scoping, first-run registration | Built |
| Peer sync — HTTP transport, Ed25519 envelopes, folder/USB | Built, not yet exercised beyond its test suite |
WRAP trades/v0 binding |
In progress — cross-organisation work orders |
| Tenant portal | Designed, not built. The data model carries public vs internal event visibility; there is no tenant-facing surface yet |
| Photo attachments | Partial — references exist; content-addressed blob storage and replication are not built |
| Template versioning | Open question. Comparison tolerates template drift by pairing on item text; there is no formal versioning |
| Recurring / planned maintenance | Not built |
| Compliance certificates | Not built |
| Flag | Default | Description |
|---|---|---|
--db |
pango.db |
SQLite file path |
--addr |
127.0.0.1:8099 |
Listen address |
--demo |
off | In-memory demo data; forces :memory: so it can never touch a real database |
--sync-listen |
off | Accept sync from enrolled peers |
--sync-peer |
— | Peer URL to sync with |
--sync-folder |
— | Shared directory for file transport |
Every networked feature is off by default. A fresh install makes no outbound connections.
| Doc | |
|---|---|
| Architecture | The binding contract — read before changing anything structural |
| Getting started | Install and first run |
| Configuration | Flags and settings |
| Sync | The replication protocol in depth |
| WRAP | Cross-organisation work orders |
| Inspections | Condition capture and comparison |
| Self-hosting | Deployment |
| Cloud & tunnelled nodes | Running on a public address — a different threat model |
| Threat model | Including what is not protected |
| FAQ |
npm run dev # frontend on :5173
npm run build # production bundle
npm test # vitest
npm run test:e2e # playwright, against the real binary
npm run screenshots # regenerate docs/screenshots from demo mode
make check # the full gate
cd backend && go test ./...Issues and pull requests welcome. Read ARCHITECTURE.md first — several of its rules (append-only money, building-as-authority, author-key tie-breaks) look like style preferences and are not.
The mark in brand/ is the source of truth. Every icon this repo
ships — favicon, PWA and app icons, the mark in the README and on the site — is
rendered from brand/logo.svg rather than redrawn, so there is one approved
drawing and no second copy to drift.
Copy it outward, never edit a derived copy, and never edit brand/ to match
something downstream.
MIT OR Apache-2.0 — © VulOS. Pango is a VulOS project; source and issues at github.com/vul-os/pango.
![]()
vulos — open by design









