Contributing
Before starting on something large, open an issue to discuss it.
Development setup
Section titled “Development setup”You need uv; it installs the Python version the project uses.
git clone https://github.com/Latet-AI/latet.gitcd latetuv syncRun Latet from the checkout with uv run:
uv run latet serve # the daemon in the foreground, with its log in the terminaluv run latet claude # Claude Code with the plugin from src/latet/claude_pluginuv run latet doctorTo keep a test instance apart from a Latet you use for real, set LATET_DATA_DIR and
LATET_CONFIG to a scratch folder and choose another port in that config.yaml.
latet serve --passthrough forwards traffic without masking, and --capture <folder> writes
every exchange to local files (credential headers redacted), which helps when you study a
protocol. Captures hold conversation content: never commit them (notes/captures/ is ignored by
git for this reason).
Checks
Section titled “Checks”Run these before opening a pull request. CI runs them in its lint and test jobs, the tests on
Windows, macOS and Linux with more property-test examples (HYPOTHESIS_PROFILE=ci). CI also runs
uv run python scripts/gen_notices.py --check, the server tests, and the plugin, dashboard and
documentation checks described below, and it builds the installers and the container image.
uv run pytestuv run ruff check src tests scriptsuv run ruff format --check src tests scriptsuv run pyrightuv run python scripts/gen_reference.py --check- Reference pages. The CLI, configuration
and detectors pages are generated from the code. After changing a
command, a configuration key or a detector, regenerate them with
uv run python scripts/gen_reference.py. Never edit them by hand. - Claude Code plugin.
claude plugin validate src/latet/claude_pluginvalidates the plugin with a compatible installed Claude Code.claude plugin test, run insrc/latet/claude_plugin/, is not available in every installed runtime; use it only when that runtime exposes the test command. - False positives.
tests/unit/test_fp_budget.pyruns the detectors over Python’s standard library, which holds no real secrets, with a budget per profile that may only go down.uv run python scripts/fp_corpus.pyprints what fires on a larger corpus, rule by rule. - Third-party notices. After changing dependencies, run
npm ciindashboard/and indocs/, then regenerate them withuv run python scripts/gen_notices.py. Add--checkto verify that the file is up to date without rewriting it; CI runs that check.
Ground rule: tests use synthetic data only. Never commit real secrets, real personal data or traffic captures.
Server tests
Section titled “Server tests”Use Docker and the existing helper for isolated test PostgreSQL, Valkey and PostgreSQL with TLS. The first start downloads their images if absent:
uv run python scripts/server-services.py upuv run python scripts/server-services.py envSet the printed LATET_TEST_DATABASE_URL, LATET_TEST_VALKEY_URL and
LATET_TEST_TLS_DATABASE_URL in the test shell, using that shell’s assignment syntax.
They name disposable test services, never production data. Set LATET_TEST_REQUIRE_SERVICES=1,
then run:
uv run pytest -q -m serverEach test uses its own database schema and Valkey prefix. CI’s server job provides and
requires the services: an unavailable service must fail rather than silently skip.
Stop your helper-owned services with uv run python scripts/server-services.py down;
coordinate shared services with their owner.
Real-agent tests are opt-in with LATET_AGENT_TESTS=1 and uv run pytest -m agents.
They run installed third-party programs against local fake providers, but those programs
may contact their own external hosts. Use isolated test homes and synthetic credentials;
local fixtures do not guarantee zero external traffic. Tests marked live use the local name
and text recognition models: they are skipped where those models are not installed, as in CI.
Dashboard
Section titled “Dashboard”The dashboard lives in dashboard/ (React, Vite and i18next) and needs
Node.js 22.12 or newer.
cd dashboardnpm cinpm run dev:mock # the dashboard with sample data, no daemon needednpm run lintnpm run check:i18n # every key the code uses is in en.json, and every language has its keysnpm testnpm run build # writes the files the daemon serves into src/latet/web/A new string goes in en.json and, translated, in the other languages’ files, in the same change:
check:i18n wants the same keys in every language.
The tutorial
Section titled “The tutorial”The chapters of latet tutorial are YAML files in src/latet/tutorial/chapters/: each step’s
prompt and explanation, the pretend model’s answer, and the checks that must hold after it. The
tests play every chapter from start to end through the real masking, twice, so a change to Latet
that makes a chapter untrue fails them: change the chapter with the behaviour. They also refuse a
chapter that names a side of the screen (on a narrow screen the page stacks its panes) or holds a
value a secret scanner would take for a provider’s key.
From a checkout, uv run latet tutorial needs the dashboard built (npm run build in
dashboard/). The dashboard’s mock mode plays the chapters too, with ?mock-tutorial, from its own
copy in dashboard/src/mock/tutorial.ts: change it with the chapter.
Documentation
Section titled “Documentation”The site lives in docs/ and is built with Starlight. You need
Node.js 22.12 or newer.
cd docsnpm cinpm run dev # http://localhost:4321/npm run build # static site in docs/dist/Pages are Markdown or MDX files in docs/src/content/docs/. The sidebar is defined in
docs/astro.config.mjs.
English is the reference version. A pull request that changes what Latet does updates the English pages it affects, in the same pull request: a new option, command or behaviour is documented in English before it is merged. Translations follow the English pages, never the other way round. A pull request may change the English pages alone: the maintainers bring the other languages in line.
The dashboard’s screenshots are made by a script, from its sample data, in every language and both
themes: after a change that shows on one of them, run npm run screenshots in dashboard/
(npm run screenshots -- live it makes some only). The first time, npx playwright install chromium
fetches the browser it uses. An MDX page shows one with <Screenshot name="live" alt="…" />, after
importing docs/src/components/Screenshot.astro as dashboard.mdx does.
How to add a language
Section titled “How to add a language”Documentation
Section titled “Documentation”The documentation exists in five languages: English at the root, the reference, and Italian,
German, French and Spanish under /it/, /de/, /fr/ and /es/ (the locales in
docs/astro.config.mjs). A page not translated yet shows the English one, with a notice. The
reference pages generated from the code stay English in every language.
- Translating a page is creating it under
docs/src/content/docs/<lang>/, at the same path as the English one:concepts/how-it-works.mdxbecomesit/concepts/how-it-works.mdx. Its imports and image paths go one../deeper, since the file is one folder further down. - Links between pages stay relative, so a translated page keeps them in its language. A
translated heading changes its anchor, so the links to it change too:
scripts/check_docs_links.pyfinds the ones missed (run it from the repository’s root afternpm run build; CI runs it too). - The interface strings Latet adds are in
docs/src/content/i18n/<lang>.json, and the sidebar’s labels in thetranslationsofdocs/astro.config.mjs. Starlight’s own interface is already translated. - Another language is one more entry in
locales, itsdocs/src/content/i18n/<lang>.json, and its sidebartranslations.
Run npm run build and check the result with npm run preview.
Dashboard
Section titled “Dashboard”The dashboard comes in English, Italian, German, French and Spanish. To add another (Portuguese here):
- Copy
dashboard/src/locales/en.jsontopt.jsonand translate the values. Keep the keys and the{{variables}}, and add every plural form the language needs (key_manyin Portuguese). - Run
npm run check:i18n: it lists the keys missing or extra, the variables that differ and the plural forms the file lacks. - Register it in
dashboard/src/i18n.ts, with its name in its own words (LANGUAGE_NAMES), and add it to the languagesdashboard/src/i18n.test.tsexpects. That test file uses Portuguese as a language the dashboard lacks (pt-BR,docsUrl(…, 'pt')): change those cases to another language. Until the documentation comes in the new language, makedocsUrllead to the English pages for it; once it does, add the language toLANGUAGESindashboard/scripts/screenshots.mjs.
The dashboard opens in the language the reader chose in its menu, else in the first of the browser’s
languages it has, else in English. dashboard/README.md has the details.
Tutorial
Section titled “Tutorial”A chapter’s texts in another language are in src/latet/tutorial/chapters/<lang>/<chapter>.yaml:
its titles, explanations, prompts, the agent’s lines and the model’s turns, step by step, each with
the placeholders of its English text. The rest of the chapter, its values and its checks, comes
from the English file. Whoever changes a chapter’s English texts changes them in every language.
The tests play every chapter in every language, and hold each language to its own editorial rules
(tests/unit/tutorial/test_chapters.py). A new language also needs its own rules in that file, in
ON_SCREEN and JARGON, or the tests fail.
A pull request may change a chapter’s English texts alone. A text that gains or loses a placeholder needs the same change in its other languages, or the tests fail; the maintainers translate the rest.
How to add a country
Section titled “How to add a country”National ID and tax numbers live in src/latet/engine/detectors/ids/regions/, one module per
country, each a tuple of IdSpecs. VAT numbers live in ids/vat.py.
- Write
regions/<cc>.py(the ISO 3166-1 alpha-2 code): for each number, a pattern for how it is written, a validator, a generator of valid fakes in the same layout, its keywords, and when a keyword is required (context:never,bare,codeoralways). Write the validator from the issuing authority’s public specification. python-stdnum is a test-only reference under the LGPL: never copy its code. - Register the module in
regions/__init__.py, and the country inID_REGIONSinsrc/latet/config.py. - Add a layout and the stdnum module, if there is one, to
tests/unit/test_ids_oracle.py, and a keyword in thetest_ids.pycases if needed. The oracle test checks 200 fakes per type and compares the validator with stdnum next to valid numbers; list deliberate differences there. - Add the type’s label to the dashboard’s five languages,
dashboard/src/locales/<lang>.json(rules.pii.types), toPII_TYPESindashboard/src/lib/labels.tsand its description toPII_HELPindashboard/src/pages/RulesPage.tsx; add the type and the country to the mock’s catalog too (PII_CATALOGandid_regions_availableindashboard/src/mock/fixtures.ts,ID_TYPESindashboard/src/mock/builder.ts). Then runuv run python scripts/gen_reference.pyfor the reference tables. - Run the false-positive budget (
tests/unit/test_fp_budget.py): no ID rule may fire on the standard-library sample.
How to measure a local reviewer
Section titled “How to measure a local reviewer”Which local model works best as a local reviewer
is still open, and your measurements are welcome. Serve a model on this computer (llama.cpp, Ollama
or LM Studio), point LATET_LLM_URL and LATET_LLM_MODEL at it, and run the benchmark on its 93
invented cases:
uv run python scripts/llm_review_bench.py run --label <model> --variants Auv run python scripts/llm_review_bench.py reportSend what it wrote in benchmarks/llm-review/ (your rows in results/, and tables.md) in a pull
request, with your graphics card or CPU, the server and the model’s quantization.
benchmarks/llm-review/README.md says how the published numbers were made.
License
Section titled “License”Latet is licensed under the Latet Sustainable Use License, its Enterprise Edition under the Latet Enterprise License, and Latet is also offered under commercial licenses (LICENSING.md). Contributions are accepted under the contributor license agreement: you keep your copyright, and your contribution can be part of Latet under all of them. To agree, tick the box “I agree to the Latet contributor license agreement (CLA.md).” in the template of your first pull request.
© 2026 Emanuele Casabona · VAT IT02293040990 · Privacy policy · Legal notice · latet.ai