Initial commit on Forgejo
Fresh repository history for elektrine/tarakan hosted at https://git.elektrine.com/elektrine/tarakan.
This commit is contained in:
commit
af6077b9c3
455 changed files with 78366 additions and 0 deletions
131
CLAUDE.md
Normal file
131
CLAUDE.md
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
# 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue