Skip to main content

Actenon Scan

What can your AI agent do without permission?

Actenon Scan finds where model-controlled input reaches actions that change, delete, send, deploy, or spend — without a recognised dominating authority check. It parses Python, TypeScript, and Go. Other languages are not supported (and are reported as unsupported, never silently treated as clean).

License: Apache-2.0

Python 3.10+

PyPI: actenon-scan Dependencies: 0 GitHub Action SARIF CI claims: machine-verified Code style: ruff Vendor-neutral

Soundness Challenge

Scan has known blind spots — custom agent loops, action/observation dispatchers, dynamic dispatch, the per-file analysis limit, the parameter-binding limitation for assert-style guards. The full list is in docs/COVERAGE.md. Silence must never imply safety: the boundary is published, not hidden.

If you find a case scan gets wrong, submit it. The template is at community-scans/CHALLENGE_TEMPLATE.md; the live scoreboard (open and fixed cases) is at docs/SOUNDNESS_CHALLENGE.md. We commit to responding to every submission within 30 days. Accepted challenges become permanent regression tests when fixed.


Three ways to use it

Run locally — no account, no cloud upload, zero-config first run:

uvx actenon-scan scan .

Add to GitHub — one command generates a non-blocking workflow:

uvx actenon-scan install github

Request a reviewed scan — a maintainer-requested, manually adjudicated scan with private-first delivery:

See SCAN_ME.md.


Quick start

uvx actenon-scan scan .

No install, no config, no cloud account. The first run produces a blast-radius summary — what your agent can reach and whether a recognised guard sits on the path:

YOUR AGENT'S BLAST RADIUS

Consequential capabilities: 12
  Guard found on path:       9
  Review required:           3

Your agent can reach 3 consequential actions without a dominating authorization check.

  REPOSITORY       2   create_file, merge
  MONEY            1   process_refund

Most exposed: app/tools.py:47  process_refund()
  Reachable by:              @mcp.tool()
  Consequence:               MONEY
  Guard evidence:            none found on the analysed path
  Model-controlled inputs:   payment_intent, amount

3 findings in 340 files (0.42s)
Next:
  actenon-scan explain app/tools.py:47
  actenon-scan fix app/tools.py:47

Repository analysis: 1 transitively-reachable sink caught (would have been
missed by per-file scan); 184 unresolved agent-entrypoint calls not followed
(dynamic dispatch / external modules).

What this means: the scanner found 12 consequential capabilities in your code — actions an agent can reach that change, delete, send, deploy, or spend. 9 have a recognised guard on the path. 3 do not, and those are the findings above.

Your code stays local. The scanner parses source files on your machine. Nothing is uploaded. The GitHub Action runs in your repo's CI — same boundary.

No runtime required. Actenon Scan is a static analyser. It does not require the Actenon Kernel, Permit, or any runtime adoption. The fix command generates repository-native guards (your own authorize() convention) by default. Actenon enforcement is one option among several, not a dependency.

Then dig deeper:

actenon-scan explain app/tools.py:47    # show the analysed execution path
actenon-scan fix app/tools.py:47        # generate a remediation diff
actenon-scan brief app/tools.py:47      # one-page report for outreach
actenon-scan scan . --format html       # self-contained HTML report
actenon-scan scan . --format markdown   # paste into a GitHub issue
actenon-scan scan . --format sarif      # upload to GitHub code scanning

What it does: finds consequential actions an AI agent can reach — payments, file deletions, shell commands, database writes, deployments, email sends, repository mutations — and checks whether a recognised authority guard dominates the path.

What it does not do: it does not prove your codebase is secure. It does not verify guards outside the analysed file. It does not assess runtime behaviour or network configuration. A clean scan means no supported consequential-action paths were identified — not that the codebase is safe.

Supported languages: Python, TypeScript, Go. Zero runtime dependencies. No Actenon account required.

Repository-level analysis (default on)

For directory targets, scan runs a second pass on top of the per-file analysis: it builds a repository symbol index, an interprocedural call graph with explicit RESOLVED/HEURISTIC/UNRESOLVED edge certainty, computes transitive reachability from @tool entrypoints (depth 32), and propagates function effect summaries through strongly-connected components to a fixed point. This is what catches a sink in a helper that the per-file scan misses because the helper is not itself @tool-decorated — the canonical case is @mcp.tool() def my_tool(): helper() where helper does requests.post(...).

The repository layer has been on by default for directory targets since commit 25ee4f9, first released in v1.5.0 — the v1.4.0 package on PyPI predates it, has no repository layer and does not accept --no-repository-analysis. It is opt-out:

actenon-scan scan . --no-repository-analysis    # per-file scan only

The repository layer augments per-file findings; it never suppresses them. New findings are emitted only when the transitive path is fully RESOLVED. Paths with HEURISTIC edges are recorded as evidence but produce no new findings (conservative — never silently promote unknown to safe).

The disclosure line

Every scan that runs the repo layer prints a one-line disclosure at the end of the output. It appears in all six output formats — pretty, list, json, sarif, markdown, and html (in JSON and SARIF it is carried as the transitive_followed_count / transitive_unfollowed_count fields plus repository_analysis_enabled; in the human-readable formats it is rendered as the trailing "Repository analysis:" line). When the repo layer is disabled with --no-repository-analysis, the disclosure is omitted.

Repository analysis: N transitively-reachable sinks caught (would have
been missed by per-file scan); N unresolved agent-entrypoint calls not
followed (dynamic dispatch / external modules).

The two counts mean different things and must not be divided:

  • transitive_followed_count (the first N) — the number of new findings the repository layer emitted that the per-file scan missed. These are sinks in helpers that became reachable only through a resolved transitive call chain.
  • transitive_unfollowed_count (the second N) — the number of unresolved call sites directly out of agent entrypoints, including every call to an external library (subprocess.run, requests.post, json.loads, etc.). It is a count of unresolved call sites, not a resolution rate: dividing the first number by the second produces a ratio with no physical meaning (findings ÷ call sites).

A CI gate (tests/test_repository_disclosure_gate.py) asserts the disclosure appears when the repo layer is enabled and is omitted when disabled.

What Actenon Scan detects

Actenon Scan finds places where a model-controlled or agent-controlled parameter reaches a consequential action — a payment, a repository mutation, a file deletion, a shell command, a database write, an email send — without a dominating authority check in the analysed path.

Consequence categories detected:

Category Examples
REPOSITORY PyGithub create_file/delete_file, raw GitHub REST (POST/PUT/PATCH/DELETE to api.github.com/repos)
VCS GitPython repo.push/commit/reset, git.push/commit/reset (distinct from GitHub repo-mutation above)
MONEY Stripe refunds, Braintree charges, generic payment SDK calls
DATA LOSS SQL DELETE, s3.delete_objects, os.remove/os.unlink/shutil.rmtree
EXECUTION eval/exec, container.exec_run, docker exec
SHELL subprocess.run/Popen/call, os.system/popen, pty.spawn (distinct from eval/exec above)
DATABASE psycopg2/sqlite3 execute/executemany/executescript with caller-controlled SQL
EGRESS requests/httpx/aiohttp POST/PUT/DELETE to external URLs
MESSAGING SMTP sendmail/send_message, Slack, Twilio, SNS, SES, SendGrid, Resend, Postmark
IDENTITY create_user, assign_role, rotate_keys, update_permissions (identity/account lifecycle)
ACCESS CONTROL IAM put_user_policy, attach_role_policy, create_role, create_access_key, update_assume_role_policy
SECRETS Secrets-manager reads (get_secret_value, get_parameter, read_secret)
DEPLOYMENT kubectl/kubernetes-client, terraform, helm
FILE File writes, chmod, rename, os.OpenFile (Go)
BROWSER Playwright page.click/fill/submit, Selenium send_keys
PROVIDER Provider-SDK calls (aws-sdk, gcp, azure)

What it does not establish

The scanner may establish that:

  • an agent or model-controlled parameter reaches a consequential action;
  • no dominating authority check was found in the analysed path;
  • a recognised sink is present.

The scanner does not automatically establish that:

  • an attacker can externally reach the agent;
  • no guard exists outside the analysed file or supported architecture;
  • exploitation is practical;
  • the operation is irreversible;
  • the finding is a vulnerability.

How to explain a finding

actenon-scan explain path/to/file.py:42

Shows the analysed execution path: agent entry point → model-controlled inputs → execution path → guard evidence → consequence. Includes a mandatory "What this does NOT establish" section.

How to generate a fix

actenon-scan fix path/to/file.py:42              # auto-select mode, print diff
actenon-scan fix path/to/file.py:42 --mode guard     # repository-native guard
actenon-scan fix path/to/file.py:42 --mode approval  # framework approval
actenon-scan fix path/to/file.py:42 --mode actenon   # Actenon proof verification
actenon-scan fix path/to/file.py:42 --apply          # write the change

Remediation is offered in neutral order: repository-native guard first, framework-native approval second, Actenon proof verification third. Actenon is not forced into every recommendation.

How to add it to a PR

GitHub Action (sticky comment + SARIF, zero config)

# .github/workflows/actenon-scan.yml
name: actenon-scan
on:
  pull_request:
  push:
    branches: [main]
  schedule:
    - cron: '0 6 * * *'
jobs:
  scan:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write      # for the sticky comment
      security-events: write    # for SARIF upload to Security tab
      contents: read
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # required for --changed-only to diff against the base branch
      - uses: Actenon/actenon-scan@v1

That's it. The action:

  • Scans only changed files on PRs (full tree on push/schedule)
  • Posts a sticky blast-radius comment on PRs with findings (updated in place)
  • Uploads SARIF to the GitHub Security tab
  • Does not fail the build by default

Ready to enforce? Add fail-on: high once you've triaged your baseline:

      - uses: Actenon/actenon-scan@v1
        with:
          fail-on: high

All inputs

Input Default Description
path . Path to scan
fail-on none Fail at this severity (none/low/medium/high). Default: none. The CLI default is medium (intentionally different — see Exit codes below).
config "" Path to config file
baseline "" Path to baseline.json for known-findings suppression
scan-scope auto changed (PR only), full (entire repo), or auto
fail-on-unsupported false Fail when unsupported source files (e.g. .rb, .java) were found and not scanned
comment-on-pr true Post sticky blast-radius comment on PRs
upload-sarif true Upload SARIF to Security tab
version "" Pin scanner version (default: action's own version)

Outputs

Output Description
findings-count Total findings
high-count HIGH-severity findings
medium-count MEDIUM-severity findings
low-count LOW-severity findings
sarif-path Path to the SARIF file

Pre-commit hook

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/Actenon/actenon-scan
    rev: v1.4.0    # pin to the latest release tag; see https://github.com/Actenon/actenon-scan/releases
    hooks:
      - id: actenon-scan
        args: [--changed-only, HEAD]
      # Optional: scan TypeScript/JavaScript and Go files too. Requires
      # the [typescript] / [go] extras (auto-installed by pre-commit).
      - id: actenon-scan-typescript
      - id: actenon-scan-go

The default workflow does not block merging solely because findings exist unless you explicitly configure fail-on: high. It does fail if the scan itself does not complete (a usage/config error, a crash, or a --changed-only base ref git cannot diff against): a scan that produced no result is never reported as a green, zero-finding check.

Exit codes

Code Meaning
0 No findings at or above the --fail-on threshold
1 Findings at or above the threshold (or --fail-on-unsupported and unsupported files found)
2 Usage error (bad arguments, missing file)

Unsupported files alone do not trigger exit 1 unless --fail-on-unsupported is passed. A scan that finds 0 supported files but N unsupported files exits 0 with a visible warning in the output.

CLI vs Action: the --fail-on default intentionally differs

Surface Default Why
CLI (actenon-scan scan .) medium The CLI's exit code is its only machine-readable signal. A scanner that finds 8 unguarded consequential actions and returns 0 passes CI silently — the false-assurance failure this tool exists to close. medium is the near-universal convention for SAST/linter tools (semgrep, bandit, eslint, shellcheck all do it).
GitHub Action (uses: Actenon/actenon-scan@v1) none The Action has its own reporting surface — a sticky PR comment + SARIF upload to the Security tab. Findings stay visible even when the check is green. A soft default is defensible for teams adopting the tool against an untriaged baseline. Set fail-on: high once you've triaged.

Do not "fix" them back into alignment — the two surfaces have different reporting capabilities, and the defaults reflect that.

How to export reports

actenon-scan scan . --format html --output report.html     # self-contained HTML
actenon-scan scan . --format markdown --output report.md    # GitHub issue / PR
actenon-scan scan . --format sarif --output report.sarif    # GitHub code scanning
actenon-scan scan . --format list                           # old linter-style list
actenon-scan scan . --format json --output report.json      # machine-readable

HTML reports are self-contained (no external scripts, fonts, or assets), safe to open locally, and include the visible honesty statement: "What this scan verified / did not verify".

How to suppress or baseline known findings

# Suppress a single finding inline — BOTH syntaxes are accepted:
# actenon-scan: ignore[REPOSITORY-MUTATION]
# actenon-scan: suppress REPOSITORY-MUTATION
# Baseline known findings:
actenon-scan scan . --baseline known-findings.json

# Generate a baseline from the current scan (locks in today's findings
# so only NEW findings appear in future scans):
actenon-scan baseline . --output known-findings.json

Scanning a repo that contains test fixtures

If your repo contains security test fixtures — deliberately unguarded code used to test the scanner or your own security controls — add an .actenon-scan.json at the repo root to exclude them:

{
  "_comment": "Exclude test fixtures that contain intentionally unguarded code.",
  "exclude": [
    "tests/benchmark/**",
    "tests/corpus/**",
    "tests/fixtures/**"
  ]
}

The file is auto-detected by actenon-scan scan . — no --config flag needed. This repo ships one itself; see .actenon-scan.json.

How to configure custom guards

# Register a guard by name without a config file:
actenon-scan scan . --guard my_authorize --guard require_permission

# Or use a config file:
actenon-scan scan . --config actenon-scan.json
actenon-scan init --format json  # write a default config

Custom sinks and reachability signals (config-only, no code changes)

You can extend detection without forking the repo. The config file's sinks and reachability keys are merged with the defaults at load time:

{
  "guard_patterns": ["my_authorize"],
  "sinks": [
    {
      "id": "MY-PAYMENT-CHARGE",
      "category": "payments",
      "severity": "high",
      "description": "Custom payment SDK charge call",
      "match": {
        "type": "qualified_call",
        "qualified_names": ["my_payments.charge"]
      },
      "priority": 20
    }
  ],
  "reachability": {
    "tool_decorators": ["my_agent_tool", "component"],
    "tool_base_classes": ["MyAgentBase", "dspy.Module"]
  }
}

This is how you add support for an unrecognised agent framework (e.g. Haystack @component, DSPy dspy.Module, ControlFlow @task) without waiting for an upstream release. See default_rules.json for the full schema and CONTRIBUTING.md for the match-type reference.


Machine-verified package claims

The claims: machine-verified badge links to a CI gate (verify-claims.yml) that fails on every PR, push to main, and once a day if any of these specific factual claims about the package itself stop being true:

  • Zero runtime dependencies — scan's single most important credibility claim (it is what lets a security team deploy scan unilaterally into a codebase that has adopted nothing). Read from pyproject.toml, not prose.
  • Install commands — every pip install in this README is resolved against the live registry; the Python version badge is generated, not hand-edited.
  • The ecosystem table — rendered from the protocol's ecosystem.yaml, never hand-edited.

The badge does not verify claims about scan's detection coverage, recall, precision, or safety. Those claims are verified by the benchmark gate (benchmark.yml) and the corpus triage gate (check_corpus_triage.py), not by verify-claims.yml.


The Actenon ecosystem

Scan is one of the independent repositories that together close the execution gap — the gap between upstream authorization and the execution edge that actually performs a consequential side effect.

Repository Role Depends on Packages
actenon-protocol The neutral wire contract — what every artefact looks like on the wire — actenon-protocol (PyPI) · @actenon/protocol-types (npm)
actenon-kernel The open verifier — defines what a valid proof is actenon-protocol actenon-kernel (PyPI)
actenon-permit The developer on-ramp and authority broker actenon-kernel, actenon-protocol actenon-permit (PyPI) · @actenon/sdk (npm)
actenon-scan ← you are here The independent static-analysis scanner — actenon-scan (PyPI)

Optional: actenon-cloud — a managed control plane (source-available; see its LICENSE). Not required by any component above; every capability in this ecosystem works without it.

Also available: sdk-go (Go verifier SDK) and sdk-rust (Rust verifier SDK) for non-Python integrations.

Performance

Measured against a pinned real repository, not a synthetic tree: langchain-ai/langchain @ fa7ce76, 1,954 Python files scanned. The pin, every figure below, and the raw crossover table live in tests/benchmark/perf-fixture.json.

Scan time depends on your core count, so the range is stated across the machines actually measured rather than as a single number you might not be able to reproduce:

langchain, 1,954 files serial default (auto)
10-core Apple Silicon 1.9s 0.96s
2–4 core CI container ~5.2s ~5.2s (stays serial)

Other repositories on the 10-core host: crewai (957 files) 2.4s → 1.1s, openai-agents (539) 2.1s → 0.67s, mcp-python-sdk (586) 0.6s → 0.29s.

Why the default is not always parallel

Parallelism is not free, and it does not pay off everywhere. Measured, it is ~2.5x faster on a 10-core machine and ~10% slower on a 2–4 core CI runner, where there are no spare cores to absorb the workers.

So --jobs auto-tunes: it parallelises only at 8+ cores and 250+ files, and stays serial otherwise. The 5–7 core range is unmeasured and therefore treated as serial — the default is the mode that is never slower, not the one that is sometimes faster.

--jobs N always overrides the auto-tune. --jobs 1 forces serial. Findings are identical either way; that equivalence is a test, not a claim.

Pre-commit path: --changed-only scans a 1–3 file diff in ~150ms.

What Scan recognises as a guard

Scan recognises three classes of guards. A sink is "guarded" if a recognised guard call appears lexically before the sink in the same function body, OR a recognised guard decorator wraps the function containing the sink. (This is a v1 lexical-precedence heuristic — documented limitation.)

30+ vendor-neutral guard patterns

Recognised out of the box, no Actenon required:

  • authorize, check_permission, require_permission, has_role, require_role
  • verify_proof, verify_token, verify_signature
  • jwt_required, require_jwt, validate_jwt
  • opa_eval, casbin_enforce, cedar_authorize
  • verify_api_key, verify_mtls, verify_client_cert
  • require_auth, require_authz, auth_required
  • can, may, is_allowed, check_access
  • authorize_request, authorize_action
  • …and the rest in actenon_scan/detectors/guards.py

Actenon-specific guards

Recognised when you are using the Actenon ecosystem:

  • verify_pccb, PCCBVerifier, PCCBVerifier.verify
  • ProtectedExecutor, ProtectedExecutor.execute
  • ActenonGate, ActenonGate.protect, ActenonGate.protect_action, @protected_mcp_tool
  • Actenon, Actenon.local, Actenon.cloud
  • Broker, Broker.execute, Broker.execute_via_adapter
  • Gateway, Gateway.execute
  • BoundaryMiddleware, BoundaryVerifier, BoundaryVerifier.verify_boundary

Constructing one of these clients (Actenon.local(...), ProtectedExecutor(...)) is not itself a guard — the verify/execute/protect call made through it is. A side effect inside a callback that is only ever handed to a typed ProtectedExecutor.execute / ActenonGate.protect (etc.) call counts as guarded; the same callback passed to an untyped x.execute(...) does not.

Custom guards

Register your own guard function names:

actenon-scan init
# Edit actenon-scan.json:
# {
#   "guards": ["my_internal_check", "company_authorize", "acme_can"]
# }
actenon-scan scan ./my-agent-code

Custom guards are first-class — Scan does not privilege Actenon guards over yours.

Remediation routes — 7 per finding, 3 are Actenon-free

Each finding ships with seven remediation routes. Scan does not pretend Actenon is the only answer.

  1. Add an existing internal guard. If your codebase already has a guard function Scan didn't recognise, just add it via actenon-scan init and the finding disappears.
  2. Register the guard with Scan. Same as above — Scan treats your guards as first-class.
  3. Use Actenon Kernel (proof verification). For when you want proof-bound execution at the resource boundary. The Kernel is the trust anchor; it works without Permit or Cloud.
  4. Use brokered Actenon protection (Permit + adapter). For when you control the agent framework and want credentials never to reach the agent. The recommended developer on-ramp.
  5. Use resource-owned verification. For when the resource itself is the protected endpoint (FastAPI route, Express endpoint, Go handler). The Boundary Kit automates this.
  6. Use Cloud-managed Actenon. For when you want a hosted control plane with 9-layer evidence bundles. Optional.
  7. Redesign the boundary. Sometimes the right answer is to remove the consequential action from the agent's reachable tool surface entirely.

Cloud is optional. Local brokered protection (route 4) works without any Cloud login.

What's in this repo

Component Location
CLI entry point actenon_scan/cli.py
Scan engine (AST walk + sink detection + guard check) actenon_scan/engine.py
Sink detector (consequential-action rules) actenon_scan/detectors/sinks.py
Guard detector (vendor-neutral + Actenon + custom) actenon_scan/detectors/guards.py
Reachability analysis actenon_scan/detectors/reachability.py
Default rules (16 categories) actenon_scan/rules/default_rules.json
Rule loader actenon_scan/rules/loader.py
Baseline (known-findings suppression) actenon_scan/baseline.py
Suppression directives actenon_scan/suppress.py
Brief (one-page outreach report) actenon_scan/brief.py
Explain (execution-path analysis) actenon_scan/explain.py
Fix (remediation diff generator) actenon_scan/fix.py
Pretty reporter actenon_scan/report/pretty.py
JSON reporter actenon_scan/report/json_out.py
SARIF reporter actenon_scan/report/sarif.py
GitHub Action action.yml
Security policy SECURITY.md
Contributing guide CONTRIBUTING.md

Independence

Scan depends on nothing at runtime. No Permit, no Kernel, no Cloud, no Protocol. actenon-protocol is a dev-only dependency, used solely by the drift-gate test (tests/test_protocol_drift.py) to verify that Scan's guard vocabulary and refusal-code references stay in sync with the Protocol's catalogue — it is not installed when you pip install actenon-scan. See pyproject.toml.

Scan is a standalone security tool. You can adopt it without adopting anything else from Actenon, and you can stop using it without affecting any other Actenon component.

Soundness challenge

Submit a case where the scanner gets it wrong. Every accepted case becomes a permanent test fixture, credited by GitHub handle.

In scope: a supported-language sink the scanner misses, a guard in the recognised vocabulary that it fails to honour, a finding on code with no model-controlled input on the path, or any case where it reports clean on something it should not.

Out of scope: unsupported languages, guard names outside the default vocabulary where --guard resolves it, anything documented as NOT COVERED in docs/COVERAGE.md.

Submit a challenge →

The scoreboard lists every case — including the ones that are accepted but not yet fixed. The open misses are the point: a challenge that only shows fixed cases is a trophy cabinet.

License

Apache-2.0 — see LICENSE.

Metadata

Release files for actenon-scan 1.5.0

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

Source distribution (sdist)

Source distribution for actenon-scan 1.5.0
File Size Uploaded
actenon_scan-1.5.0.tar.gz 421.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for actenon-scan 1.5.0
File Interpreter ABI Platform
actenon_scan-1.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 759.4 kB

Release files / actenon_scan-1.5.0.tar.gz

Download URL actenon_scan-1.5.0.tar.gz
Size 421.4 kB
Tags Source
SHA-256 checksum
How to use checksums
6cb4a0c68901d9572fa78797bb276e27ace01c5728460aa8326e3c89f0c58849
BLAKE2b-256 checksum
How to use checksums
ea039ec44fb0edd8598624928bada19a8cd55e23a357ed1c0a8bce3f3489d9d2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / actenon_scan-1.5.0-py3-none-any.whl

Download URL actenon_scan-1.5.0-py3-none-any.whl
Size 338.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d92c9d0544bc2040ad8e4229ddf9f34a94994adb41919aa0b5c376d775394209
BLAKE2b-256 checksum
How to use checksums
f611fffb83c6047e35f8be40f0d0b6b1db928ff9e0f6ddbaa1a3bd7317c048a9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

1.5.0 This release

2 release files

1.4.0

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.4

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.0

2 release files

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