Fresh repository history for elektrine/tarakan hosted at https://git.elektrine.com/elektrine/tarakan.
131 lines
6.2 KiB
Markdown
131 lines
6.2 KiB
Markdown
# Tarakan
|
|
|
|
A public security record. Contributors run their own AI agents locally against
|
|
public repositories and publish findings pinned to exact commits; other
|
|
contributors independently re-check them. Tarakan also hosts git (smart HTTP and
|
|
SSH), runs a credits/bounty economy, and tracks the same class of bug across
|
|
unrelated codebases.
|
|
|
|
`AGENTS.md` holds the Phoenix/Elixir framework conventions and is authoritative
|
|
for those. This file is only what is specific to *this* project — the domain
|
|
words, the invariants, and the traps.
|
|
|
|
## Commands
|
|
|
|
```
|
|
mix precommit # compile --warnings-as-errors, deps.unlock --unused, format, test
|
|
mix test # runs ecto.create + migrate first
|
|
mix assets.build # two esbuild profiles, see "Assets" below
|
|
```
|
|
|
|
Run `mix precommit` before considering work finished.
|
|
|
|
## Domain vocabulary
|
|
|
|
These are coined nouns; they mean specific things and the UI capitalises them.
|
|
|
|
- **Report** (`Scan`) — one agent or human review, pinned to a commit SHA.
|
|
- **Finding** (`Finding`) — one issue inside a Report. Immutable.
|
|
- **Canonical finding** — deduplicated issue assembled from Findings that share
|
|
a deterministic fingerprint (normalized path + line_start + title). Only
|
|
exact fingerprints auto-link; no embeddings, no LLM merge.
|
|
- **Check** (`FindingCheck`) — an independent verdict on a canonical finding at
|
|
a commit: `confirmed` / `disputed` / `fixed`.
|
|
- **Job** (`ReviewTask`) — a pickup ticket for agents. Optional; a Report does
|
|
not need one.
|
|
- **Infestation** — the same finding *class* across repositories, keyed by
|
|
`pattern_key` (normalized title only, no path or line).
|
|
- **Contract** (`Bounty`) — user-facing name for a bounty. The schema says
|
|
bounty; the UI says contract.
|
|
|
|
## Invariants
|
|
|
|
Break these and the product stops meaning anything.
|
|
|
|
- **The record is public at creation.** Status is a label, moderation is a
|
|
takedown. Never add a visibility gate that hides a finding pending review.
|
|
- **Everything is commit-pinned.** A finding without a SHA is not a finding.
|
|
This is what makes regressions, archaeology and reproduction possible.
|
|
- **Verification is quorum-based, not authorial.** A submitter cannot verify
|
|
their own finding. Agent checks corroborate; they do not create quorum.
|
|
- **Contributor text reaches other people's agents.** Finding titles, check
|
|
notes, fix evidence and job descriptions are all attacker-reachable and are
|
|
fed into prompts that run on someone else's machine with their subscription.
|
|
Anything user-authored that enters an agent-facing payload goes through
|
|
`Tarakan.PromptSafety` first. The client re-sanitizes independently
|
|
(`internal/untrusted`) because it is the side that pays for an injection.
|
|
- **Money paths take row locks.** Every bounty/credit mutation reads its row
|
|
`FOR UPDATE`. The credit ledger is append-only with a partial unique index
|
|
that makes mints and refunds idempotent — rely on it rather than checking
|
|
first.
|
|
- **Denial looks like absence.** An authenticated-but-unauthorized caller gets
|
|
the same 404 as a missing record, so the API is not an existence oracle.
|
|
Unauthenticated callers get a 401 challenge instead.
|
|
|
|
## Design language
|
|
|
|
Cyberpunk night-shift terminal: flat, high-contrast, monospace, phosphor on
|
|
black. Tokens live in `assets/css/app.css` (`--color-ink`, `--color-phosphor`,
|
|
`--color-signal`, …) — use them, never literal colours.
|
|
|
|
Two rules the user enforces strictly:
|
|
|
|
- **No invented copy.** Every element shows real, useful data. No taglines, no
|
|
filler stats, no placeholder rows standing in for evidence that does not
|
|
exist. If there is nothing yet, say so tersely ("None yet").
|
|
- **No explainer captions.** A caption under every control reads as vibecoded.
|
|
Prose belongs in the sections built for it (`#how-it-works`, the policy
|
|
pages); data sections carry labels and numbers.
|
|
|
|
Section headings are plain noun phrases ("Trending infestations", "Fixes
|
|
carried"), not sentences.
|
|
|
|
## Assets
|
|
|
|
`app.js` is a **deferred ES module** (`--splitting --format=esm`), so nothing in
|
|
it runs before first paint. Two consequences:
|
|
|
|
- Anything needed before paint goes in `assets/js/theme.js`, which has its own
|
|
esbuild profile, ships as a classic IIFE, and is loaded **blocking** in
|
|
`<head>`. It must not gain `defer` or `type="module"` — a test asserts this.
|
|
- Heavy dependencies use dynamic `import()` so they become separate chunks.
|
|
three.js is 132 KB gzipped versus 43 KB for everything else; it loads only
|
|
when the hero field mounts.
|
|
|
|
CSP is `script-src 'self'` — no inline scripts, no CDNs. Vendor into
|
|
`assets/vendor/`.
|
|
|
|
## Traps
|
|
|
|
Things that have actually bitten, in this repo.
|
|
|
|
- **Never run fixtures against the test database outside the sandbox.** A
|
|
diagnostic script with `Sandbox.mode(:auto)` wrote real rows and broke tests
|
|
that assert `update_all` touches exactly one row. If tests fail oddly, check
|
|
`psql -d tarakan_test -c "select count(*) from accounts"` and the stale bare
|
|
repos under `tmp/test_hosted`.
|
|
- **Postgres truncates index names at 63 characters.** Name long ones
|
|
explicitly or the constraint you reference in a changeset will not exist.
|
|
- **`AnalyticsCache` is process-global; the database is per-test.** Test config
|
|
sets `ttl_ms: 0` to bypass it. Anything cached there needs the same.
|
|
- **Foreign keys are not indexed automatically** and cascades scan the child
|
|
table without one. Add an index with every `references(...)`.
|
|
- **Scan submission is N+1** — roughly 24 queries per additional finding in
|
|
`FindingMemory.assimilate_scan/1`. Known, measured, not yet fixed. Do not
|
|
make it worse.
|
|
- **Git SSH is disabled in production** (`GIT_SSH_ENABLED`). The host key lives
|
|
on its own volume and is backed up; regenerating it looks like a
|
|
man-in-the-middle to every existing client.
|
|
|
|
## Commits
|
|
|
|
Conventional Commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`, `test:`,
|
|
`perf:`, then a lowercase summary. One type per commit — if a change needs two
|
|
prefixes, it is two commits.
|
|
|
|
**No trailers.** Do not append `Co-Authored-By` or `Claude-Session`.
|
|
|
|
Merging to `main` deploys straight to production
|
|
(`.github/workflows/ci-deploy.yml` — no tags, no releases). A `feat:` touching
|
|
`/api` is a live API change; note in the body anything the separately-versioned
|
|
Go client (`~/tarakan-client`) must ship first.
|