Skip to main content
KAERIS

KAERIS i18n — CLI

AI localization from your terminal. Translate your app's strings files into 46 languages — locally or in CI/CD. Format-aware, placeholder-safe, and incremental (only new keys).

  • Zero dependencies — pure Python stdlib, installs in a second
  • 13 formats — JSON, YAML, .strings, Xcode String Catalog .xcstrings, Markdown/MDX, .po, ARB, Android XML, CSV (Godot/Unity), XLIFF 1.2, Java .properties, .NET .resx, Mozilla Fluent .ftl
  • Incremental — --only-new translates just the keys you added, merges the rest
  • Translation QA — flags dropped placeholders & UI-overflow risk; --verify is a back-translation and meaning check for your review: it flags lines whose meaning may have changed, with a reason and a suggested fix
  • kaeris check — an i18n firewall: fails CI if a locale is untranslated or placeholder-broken, no API call
  • kaeris quota — how much of your monthly volume is left, before a big run rather than after
  • CI-ready — GitHub Action included; open a PR with fresh translations on every push

Install

pip install kaeris
# or, without installing:
pipx run kaeris --help

Windows: kaeris is not recognized

pip puts kaeris.exe into Python's Scripts folder, and on Windows that folder is often not on PATH — with the Python install manager (pythoncore-3.14-64) it never is. pip says so in a yellow WARNING: The script kaeris.exe is installed in '…\Scripts' which is not on PATH.

Every command works without it — run the module through Python instead:

python -m kaeris translate locales/en.json --langs de,fr
py -m kaeris --version

To type plain kaeris, add the folder to your user PATH once. This prints the folder:

python -c "import sysconfig; print(sysconfig.get_path('scripts'))"

(after a per-user pip install it is python -c "import sysconfig; print(sysconfig.get_path('scripts', 'nt_user'))"). Add it in Settings → System → About → Advanced system settings → Environment Variables → Path (your user) → New, then open a new terminal. pipx install kaeris is the other way: pipx keeps its own folder on PATH (pipx ensurepath).

Config file (kaeris.json) — set it up once, forget it

kaeris init                    # generic config
kaeris init --preset next-intl # or: i18next, react-i18next, react-intl, vue-i18n, flutter-arb

Writes a kaeris.json in the current directory with source, langs, keep, context, tone, icu, only_new, out and format. Once it exists, just run:

kaeris translate

with no arguments — the CLI reads kaeris.json from the current directory (or --config PATH, passed before the subcommand) for anything you don't pass on the command line. Precedence: CLI flag > kaeris.json > built-in default — so you can still override any one setting for a single run, e.g. kaeris translate --langs es or kaeris translate --no-icu. kaeris init refuses to overwrite an existing kaeris.json unless you pass --force.

Quick start

# Translate a whole file into Spanish, French and Japanese
kaeris translate locales/en.json --langs es,fr,ja --out locales

# Only translate keys that are missing from the existing target files
kaeris translate locales/en.json --langs es,fr,ja --out locales --only-new

# Keep brand/product names verbatim in every language (glossary / do-not-translate)
kaeris translate locales/en.json --langs es,de --keep "KAERIS,GitHub,OpenRouter"
# (--glossary is the same flag, if that name is easier to remember)

# Tell the model what the app is, so ambiguous strings get the right sense
# ("Bank" the money kind, not the river kind; "Play" the game, not the theatre)
kaeris translate locales/en.json --langs de,ja --context "a mobile bank for teenagers"

# Translation QA — back-translation and meaning check for your review (verify.json lists the lines to read)
kaeris translate locales/en.json --langs de,ja --verify

# …and write the fixes it found into the files (free, offline; see "kaeris fix" below)
kaeris translate locales/en.json --langs de,ja --verify --apply-fixes

# List all supported languages
kaeris languages

Output files are written next to the source (or into --out), named by language: es.json, fr.json, ja.json (or values-es/strings.xml for Android, etc.).

Markdown and MDX are translated as documents, not as key/value files: guide.md becomes de.md, ja.md and so on. Prose is translated; everything that is not prose is not. Fenced and indented code blocks, URLs, inline code, HTML and JSX tags, MDX import/export lines, heading anchors ({#install}) and front-matter configuration (slug, date, tags) come back byte for byte — only front-matter fields a reader actually sees (title, description, summary, …) are translated. Link text is translated, link targets are not.

One limitation, said out loud: text inside a multi-line JSX component is preserved rather than translated. Guessing at the boundaries of arbitrary JSX with regexes is how you ship a broken page.

Xcode String Catalogs are the exception, because the format is: one .xcstrings file holds every language, so there is one file to write, not one per language. kaeris translate Localizable.xcstrings --langs de,fr,ja updates that catalog in place — your comments, your extraction states, languages already in the file and anything marked shouldTranslate: false are carried through untouched. Plurals are expanded to the categories each language actually needs: an English one/other becomes one/few/many/other in Russian and all six forms in Arabic, with %lld left exactly where it was.

Authentication & tiers

Tier How Chars per file Languages per run
Free (anonymous) no key 10,000 8
Free API key --key with the free key from kaeris.dev/docs 50,000 8
Pro / Team/API --key kaerisp_… or KAERIS_API_KEY 100k / 1M all 46
Lifetime (BYOK) --key and --openrouter-key sk-or-… 1,000,000, no monthly volume (you pay OpenRouter for tokens) all 46

All 46 languages are available to everyone — the free tier translates up to 8 of them per run, so --langs with a longer list is refused before anything is charged or written.

export KAERIS_API_KEY=kaerisp_xxxxxxxx
export KAERIS_OPENROUTER_KEY=sk-or-v1-xxxx   # Lifetime/BYOK only
kaeris translate en.json --langs de,uk

Prefer the environment variable to --key: a key typed on the command line stays in your shell history and shows in the process list.

Get a key at https://kaeris.dev/pricing.html. A free OpenRouter key: https://openrouter.ai/keys.

kaeris quota — how much of the month is left

Paid plans include a monthly character volume (Pro 15M, Team/API 75M, counted as characters × languages). Ask before a large run, rather than finding out from a refusal in the middle of one:

kaeris quota
# ✓ Monthly volume: 1.2M of 15M used · 13.8M left · resets 2026-09-01

Every kaeris translate prints the same line when it finishes, and raises it to a warning once 80% of the month is spent — the point at which we also email the address the plan was bought with, and again at 95%. Lifetime (BYOK) has no monthly volume: you pay OpenRouter for tokens directly.

Project translation memory

kaeris.lock remembers a key and the hash of the English behind it. That is enough to notice an edited string and nothing else: rename nav.save to toolbar.save and the identical sentence is translated and charged again; the same "Save changes" in common.json and in checkout.json is paid for twice; a colleague who just cloned the repo re-translates what you already bought.

kaeris-tm.json remembers the text:

source string + the settings it was translated under  ->  { de: …, fr: … }

It is written next to your source file, and you commit it. Reuse then survives renamed keys, strings moved between files, deleted-and-restored keys, and a fresh clone. Matching strings are handed to the API as already-known and are not charged — the same reuse path the web app uses.

The settings are part of the key deliberately: a string translated with --tone formal, a different glossary or a different model is a different answer, so changing any of them retranslates rather than serving something produced under rules that no longer apply. Use --no-tm to ignore it for one run.

What the run actually did

Every kaeris translate ends with two lines about the run itself:

→ Run: google/gemini-2.5-flash-lite · Pro · 3/3 languages · 412 strings · 18.4s
     124k charged · 38k reused free  ·  icu, verify, formal

--receipt run.json writes the same thing as JSON: model, plan, languages requested against languages actually delivered, characters charged against characters reused for free, the settings that were applied, and — the useful one — which of your glossary terms were really present in the source. A term that was never in the file was never going to be kept, and nothing used to say so; the run just looked clean.

It carries counts and your own settings, never your strings, so it is safe to commit next to the translations and read in a diff.

kaeris fix — apply what --verify found

--verify writes verify.json: every line to read, why, and — where the meaning check had one — a corrected translation. kaeris fix writes those into your translated files:

kaeris fix --from locales/verify.json            # apply
kaeris fix --from locales/verify.json --dry-run  # only say what would change
  • A suggested fix is written only if our checks pass it: every placeholder, tag and ICU branch, the line breaks and the *emphasis* of the source are still there, and nothing new is wrong.
  • A line that lost a line break or tore an emphasis, with no fix suggested, gets them put back where they fall in the source.
  • It prints what was applied and, for every line it left alone, why. A line that needs a new translation is named, not re-translated: that would be a model call, charged to your quota like any translation. Applying is free and makes no network call.
  • The file keeps its layout, encoding and line endings; running it twice changes nothing.
  • What it did is recorded in verify.json for anything that has to read it (a CI step, an agent), so nobody parses the log: _applied and _skipped, each a list of {"lang", "key", "how", "why"}. how is "fix" (the meaning check's line) or "structure" (line breaks / emphasis put back), and null for a skipped line; why says what was done or why the line was left alone. _summary.by_lang holds lines and to_review per language. kaeris fix --json prints the same on stdout (applied, skipped, counts); --dry-run writes nothing, verify.json included.
"_applied": [{"lang": "de", "key": "save", "how": "fix", "why": "suggested fix"}],
"_skipped": [{"lang": "de", "key": "greet", "how": null,
              "why": "the suggested fix was not applied: lost placeholder {name}"}],
"_summary": {"lines": 40, "to_review": 3, "error": 2, "review": 1, "unchecked": 0,
             "by_lang": {"de": {"lines": 40, "to_review": 3, "error": 2, "review": 1, "unchecked": 0}}}

kaeris check — the i18n firewall

A local, static, no-API check: compares your source file against each target locale and fails (non-zero exit) if anything's missing or broken — the kind of check no other i18n tool gates a merge on.

kaeris check                              # reads source/langs/out from kaeris.json
kaeris check --source en.json --langs de,fr,ja --out locales
kaeris check --strict                     # also fail on extra/stale keys in a target
kaeris check --json                       # machine-readable output for CI/agents
kaeris check --ci                         # same exit-code contract; a stable, named entry point for pipelines

It runs the same deterministic quality detectors that power the web app — offline, with no account and no network. RED problems fail the build; YELLOW are advisory (pass --strict to fail on those too).

RED — breaks the build (non-zero exit):

  • Missing keys — a key exists in the source but not in a target (untranslated).
  • Placeholder mismatch — a target string's placeholders ({name}, %s, %d, {{x}}, ${x}, ICU {count, plural, ...}) don't match the source's — dropped, renamed, or hallucinated placeholders crash or silently drop data at runtime.
  • Number drift — a number in the source changed or vanished in the translation (Delete 5 files → Delete 50 files).
  • Broken entities / encoding — double-encoded (&) or malformed \u escapes.
  • Lost inline tags — an HTML/markup tag (<b>, </b>, …) present in the source is gone.
  • ICU / CLDR plural completeness — the target language is missing a plural category its CLDR rules require.
  • Glossary term dropped (opt-in) — a term listed under keep in kaeris.json (or passed as kaeris check --glossary "KAERIS,GitHub") isn't carried into the translation.
  • Glossary term translated another way (opt-in) — a term of kaeris-glossary.json is in the source, and the translation does not use the word agreed for that language (see below).

YELLOW — advisory warnings (--strict to fail):

  • Empty translation — the target string is empty (or only whitespace) while the source has text. Projects often keep empty strings on purpose (a fallback language fills them), so this is a warning, not a failure.
  • Untranslated leftover — the target still reads as the source language.
  • Register / casing drift, cross-key inconsistency, and UI-overflow risk (a translation much longer than its source, relative to that language's typical expansion).
  • Extra/stale keys — present in a target but not the source.

Exit codes: 0 clean, 1 a RED problem was found (or a YELLOW under --strict), 2 bad usage (source not found, no languages given, etc.) — drop it straight into CI:

- run: kaeris check --source locales/en.json --langs es,fr,de,ja --out locales

Glossary: one word per term, per language

--keep says "never translate this". When a term must be translated, but always the same way, put it in kaeris-glossary.json next to kaeris.json:

{"terms": [
  {"term": "workspace", "pos": "noun", "note": "the shared project area, not a desk",
   "translations": {"de": "Arbeitsbereich", "fr": "espace de travail", "ru": "рабочее пространство"}},
  {"term": "KAERIS", "keep": true}
]}

kaeris translate sends the terms with the strings, and kaeris check reads every translation against them:

$ kaeris check --source en.json --langs de,fr,ru
→ Glossary: 1 term(s), 1 kept as is
✓ de: complete & placeholder-safe
✗ fr: 1 translation fault(s):
    open: glossary: "workspace" should be "espace de travail"
! ru: 1 warning(s):
    leave: glossary: "workspace" should be "рабочее пространство" — not found, not even as an inflected form; check this line
✗ 1 translation fault

Here en.json is {"open": "Open the workspace", "leave": "Leave the workspace"}; German said «Arbeitsbereich» both times, French said «bureau» once, Russian said «рабочей области».

  • In the source a term counts as a whole word: Pro is not found in "product". Set "case_sensitive": true when the case matters. English plurals count ("workspaces").
  • In the translation an inflected form passes: every word of the agreed translation is matched by its stem, so «Name des Arbeitsbereichs» and «в рабочем пространстве» are fine.
  • A miss is an error in languages whose words keep their shape (French, Spanish, Italian, Portuguese, Japanese, Chinese…). In languages whose words change shape or join into compounds (Russian, Ukrainian, Polish, Finnish, German, Arabic…) it is a warning: there "not found" can still be correct grammar, so a person decides. --strict fails on warnings too.
  • A list gives accepted variants: "fr": ["se connecter", "connexion"]; the first is the one the model is asked for.
  • keep: true entries work like --keep.
  • With --only-new, changing a term re-translates only the strings that use it and do not already say it the new way; kaeris.lock remembers what each term asked for. Strings from the project memory that say it the old way are not reused.

The file is found as --glossary-file PATH, then "glossary_file" in kaeris.json, then ./kaeris-glossary.json. A file that cannot be read or has a broken entry stops the run with exit code 2 and says which entry.

Every format translate writes

check reads the same formats translate does — JSON, YAML, .strings, .xcstrings, .po, ARB (its @-metadata is ignored), Android XML, CSV, XLIFF, .properties, .resx, Fluent and Markdown — and finds each language's file where translate writes it (de.yml, app_de.arb, res/values-de/strings.xml, de.lproj/Localizable.strings, …; --pattern for any other layout). The same checks run on all of them.

Format Compared by Plurals
JSON, ARB, YAML dotted key ICU {count, plural, …} checked against the language's CLDR forms
.strings, .properties, .resx key none in the format
.po msgid (+ msgctxt) each msgstr[n]; the header must declare as many forms as the language needs
Android XML name <plurals> read as one ICU plural, so Japanese's single form is complete and Russian's two is not
XLIFF 1.2 trans-unit id: <source> against <target> —
CSV key, one column per language (key,en,de,…), in the file itself and in the translations/<name>.csv that translate writes; a blank cell is untranslated —
Fluent message, .attribute, selector variant a plural selector is checked like ICU
Markdown / MDX the numbered blocks of the document (no keys); code, URLs and tags must survive —

Not covered: .stringsdict (use a String Catalog), XLIFF 2.0, YAML with anchors, aliases, tags or several documents (named, not guessed at: exit code 2), and Fluent placeables inside a plural arm other than { $var }. With --sarif, findings in formats other than JSON/ARB are placed on line 1 of the file.

A .xcstrings and a CSV are checked differently because the format is different: the source language and every translation live in one file, so there is nothing to open per language — both sides come out of the same parse.

kaeris check --source Localizable.xcstrings --langs de,ru,ar

Worth running even if you never translate with us: Xcode does not fail a build for a missing translation, and it says nothing at all when a Russian plural carries only the two forms English had — which reads wrong to every user whose count ends in 2, 3 or 4. Entries marked shouldTranslate: false are not demanded, and a key with no localization at all counts as its own source string, the way Xcode treats it.

Findings in your pull request

kaeris check --source locales/en.json --langs de,fr --out locales --sarif kaeris.sarif

Writes a SARIF 2.1.0 report (kaeris ≥ 0.2.12). Upload it with github/codeql-action/upload-sarif and every finding becomes an annotation on the exact line of the locale file, inside the diff — the GitHub Action takes it as sarif-file. Written even when the run is clean, so the upload step never fails on a missing file. Inline annotations are free on public repositories; private ones need GitHub Advanced Security.

CI/CD (GitHub Actions) — translations as PRs, plus an i18n firewall

Add two workflows and translations arrive as a normal pull request — reviewed and merged like any other code change, no separate translation tool to context-switch into — while a second workflow blocks merges that leave a locale incomplete or placeholder-broken.

The kaeris-translate composite action has two modes:

  • mode: translate (default) — runs kaeris translate, then a kaeris check --json before and after so it can report real numbers, and exposes outputs: changed ("true" only if a locale/lockfile actually changed — gate your PR step on this), summary (a markdown report: keys translated, a per-language matrix, placeholder-loss count, overflow-warning count when available), missing and placeholder-issues (totals).
  • mode: check — runs kaeris check --ci (no API call). Non-zero exit fails the step (and the job) if any target locale is missing keys or has a placeholder mismatch — the i18n firewall.

1. Auto-PR on every push — see translate.example.yml:

- uses: kaeris-io/kaeris-action@v1
  id: kaeris
  with:
    mode: translate
    source: locales/en.json
    languages: es,fr,de,ja
    out: locales
    only-new: "true"

- uses: peter-evans/create-pull-request@v6
  if: steps.kaeris.outputs.changed == 'true'
  with:
    title: "i18n: new translations"
    body: ${{ steps.kaeris.outputs.summary }}
    branch: kaeris/i18n-updates
    add-paths: locales/**

2. Firewall on every PR — see i18n-check.example.yml:

- uses: kaeris-io/kaeris-action@v1
  with:
    mode: check
    source: locales/en.json
    languages: es,fr,de,ja
    out: locales

Add that job to branch protection as a required status check and a PR simply cannot merge with an untranslated or placeholder-broken locale.

How incremental mode works

--only-new (JSON) parses your source and each existing translation, then translates a key if it's missing from the target or its source text changed since the last run — and merges the results back, preserving your existing translations and any non-string values (numbers, booleans). No more re-translating (and re-paying for) the whole file every time you add one string, and no more silently-stale translations when you edit an existing English string.

Change detection is powered by kaeris.lock — a small JSON file written next to your source file after every incremental run. It records a SHA-256 of each source string plus the settings that produced it: your tone, your glossary, your app context, and the model. Change any of them and the whole locale is re-translated, so it never ends up a mix of two tones or two models. Every tier runs the same model (Gemini 2.5 Flash-Lite), so a plan change alone never forces a re-translation — the lock records the model anyway, so the day we change it, you find out from a full re-run rather than from a locale quietly built by two models. The terms of kaeris-glossary.json are the exception: a changed term re-translates only the strings that use it (see "Glossary" above). It's what lets --only-new notice an edited key even though it's still present in the target; without it, a plain "is this key missing?" check would skip the key and leave the old (now wrong) translation in place. Commit kaeris.lock alongside your source file so the check works across machines/CI. Override its location with --lock PATH or "lock" in kaeris.json (default: kaeris.lock next to the source).

All flags

Anything not passed falls back to kaeris.json, then to the built-in default.

kaeris translate [FILE]

Flag What it does
--langs es,fr,ja target languages (kaeris languages lists all 46)
--out DIR where to write; default is next to the source
--only-new / --no-only-new translate only new/changed keys (JSON, ARB) — or force a full run
--lock PATH where kaeris.lock lives (default: next to the source)
--assume-current first incremental run on a project already translated: trust the existing target strings instead of re-translating everything
--keep "A,B" / --glossary "A,B" terms to carry over verbatim (two names for one flag)
--glossary-file PATH terms and how each language must say them (default ./kaeris-glossary.json)
--context "…" one line about the app, so ambiguous words get the right sense
--tone neutral|formal|casual tone of voice
--model openai/gpt-4.1 Lifetime (BYOK): the OpenRouter model to translate with; "models": {"de": "…"} in kaeris.json sets one per language. Changing it re-translates under --only-new; other plans get an error naming the plan
--icu / --no-icu hint that strings use ICU MessageFormat, so plurals/select survive
--source-lang en base language code; also detects locales/<lang>/<namespace>.json layouts
--verify back-translation and meaning check for your review; prints ⚠ meaning: lines and writes verify.json
--back-lang en language to back-translate into with --verify
--apply-fixes with --verify: write the fixes into the translated files, only where our checks pass them; same as kaeris fix afterwards
--translate-service-keys also translate keys that start with _ ("_status"); by default they are copied through untouched in JSON, YAML and ARB
--digits native|latin digits in the result: native (default) keeps what the language writes (fa ۲۰۲۷, bn ২০২৭), latin rewrites them to 0-9; placeholders are left as they are
--lang-case keep|lower case of the language code in new file and folder names (zh-Hant.json or zh-hant.json); a file or folder that already exists in another case is always written under its own name
--jobs N jobs kept running at once (default 4): several files side by side, and with --only-new the languages of one file; results are still written and reported in order. --jobs 1 sends one at a time
--quiet no progress output
--receipt PATH write a JSON record of the run — counts and settings only, no strings
--tm PATH project translation memory file (default kaeris-tm.json next to the source)
--no-tm ignore and do not update the project translation memory

kaeris check

Flag What it does
--source FILE, --langs, --out DIR what to compare (or leave them to kaeris.json)
--pattern "{lang}.json" target filename pattern — set it if your locales are named de/common.json, strings.de.json, …
--strict also fail on extra/stale keys and YELLOW warnings
--json machine-readable result
--ci same exit-code contract, a stable entry point for pipelines
--glossary "A,B" fail a target that dropped one of these terms
--glossary-file PATH fail a target that translated a term of this file another way (default ./kaeris-glossary.json)
--sarif FILE write a SARIF 2.1.0 report (needs kaeris ≥ 0.2.12)

kaeris init — --preset NAME, --force (overwrite an existing kaeris.json). kaeris quota — no flags; reads the key from --key or KAERIS_API_KEY (needs kaeris ≥ 0.2.15). Global — --key, --openrouter-key, --config PATH (before the subcommand), --version.

Environment variables

  • KAERIS_API_KEY — API key
  • KAERIS_OPENROUTER_KEY — OpenRouter key (BYOK)
  • KAERIS_API_URL — override the API base URL
  • NO_COLOR — disable coloured output
  • KAERIS_NO_UPDATE_CHECK=1 — turn off the once-a-day "new version available" line on stderr (it is already silent in CI, in --json mode and when stderr is not a terminal)

License

MIT

Metadata

Release files for kaeris 0.2.37

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for kaeris 0.2.37
File Size Uploaded Provenance Actions
kaeris-0.2.37.tar.gz 240.5 kB Without provenance Details for kaeris-0.2.37.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for kaeris 0.2.37
File Interpreter ABI Platform Actions
kaeris-0.2.37-py3-none-any.whl Python 3 none any Details for kaeris-0.2.37-py3-none-any.whl

Total release size: 381.4 kB

Release files / kaeris-0.2.37.tar.gz

Download URL kaeris-0.2.37.tar.gz
Size 240.5 kB
Tags Source
SHA-256 checksum
How to use checksums
4bc5ea39e20def74b4974df8a83d87a23d42bf248840aa47742b908b8a3890b1
BLAKE2b-256 checksum
How to use checksums
e0326dfbc128bd5edafcde6efe06b54919bb7d6c6d39306d19fea770c7535d5d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release files / kaeris-0.2.37-py3-none-any.whl

Download URL kaeris-0.2.37-py3-none-any.whl
Size 140.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5d9d0f322d3106ba5887767e7fb177b21c3ea69180a987a601efb42bc88241f0
BLAKE2b-256 checksum
How to use checksums
70a18875952006e2189b71ab7276ad971e8b24b4939874531b5d1e3df4ceb469
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Security for kaeris 0.2.37

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.37 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page