Ctrl K
ComunidadGuías…

pyproject.toml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Copyright (c) 2026 Juan Luna. All rights reserved.
# SPDX-License-Identifier: LicenseRef-Aegis-Commercial-1.0
# Licensed under the Aegis Commercial License 1.0; see LICENSE and NOTICE.
[build-system]
requires = ["hatchling==1.28.0"]
build-backend = "hatchling.build"

[project]
name = "aegis-latent-core"
version = "5.0.3"
description = "Fail-closed LLM gateway with durable signed forensic evidence and bounded response enrichment"
readme = "README.md"
license = "LicenseRef-Aegis-Commercial-1.0"
license-files = ["LICENSE", "NOTICE", "LICENSE-THIRD-PARTY.md"]
requires-python = ">=3.11"
authors = [{ name = "Juan Luna", email = "juan.c.luna04@gmail.com" }]
maintainers = [{ name = "Juan Luna", email = "juan.c.luna04@gmail.com" }]
keywords = [
    "llm", "ai-safety", "forensics", "telemetry", "audit",
    "openai", "anthropic", "gemini", "openrouter", "proxy", "entropy", "merkle", "chain-of-custody",
]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "Intended Audience :: Information Technology",
    "Intended Audience :: Science/Research",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Topic :: Security",
    "Topic :: Scientific/Engineering :: Artificial Intelligence",
    "Typing :: Typed",
]
dependencies = [
    "fastapi>=0.111.0",
    "uvicorn[standard]>=0.29.0",
    # uvloop 0.23.0 (2026-10-01) opens an io_uring instance even with
    # UV_USE_IO_URING=0, so SeccompGuard refuses to load the filter (REG-D67).
    "uvloop<0.23; sys_platform != 'win32'",
    "httpx>=0.27.0",
    "pydantic>=2.7.0",
    "pydantic-settings>=2.3.0",
    "numpy>=1.26.0,<2.5",
    "python-dotenv>=1.0.0",
    "structlog>=24.1.0",
    "protobuf>=5.0.0",
    "redis>=5.0.0",
    "requests>=2.31.0",
    "cryptography>=50.0.0,<51.0.0",
    "cbor2>=5.9.0",
    # Rate-limiter TTL eviction (MEDIUM-01 fix). Soft dependency — graceful
    # fallback to plain dict with a one-time warning when not installed.
    "cachetools>=5.3.0",
    # Security floor for transitive dep (PYSEC-2026-215 / CVE-2026-45409).
    "idna>=3.15",
    # Security floor for transitive dep via requests (PYSEC-2026-141 / -142;
    # 2.8.0 for GHSA-8988-9cw3-xx77, GHSA-gh4c-6fx4-qh6g, GHSA-vxq7-64xx-v4gw).
    "urllib3>=2.8.0",
]

[project.optional-dependencies]
# ── Storage backends ─────────────────────────────────────────────────────────
storage-sqlite   = ["aiosqlite>=0.20.0"]
storage-postgres = ["asyncpg>=0.29.0"]
storage-dynamodb = ["aioboto3>=13.0.0"]
storage-s3 = ["boto3>=1.35.0"]
storage-all = [
    "aegis-latent-core[storage-sqlite,storage-postgres,storage-dynamodb,storage-s3]",
]
# ── Enterprise connectors ────────────────────────────────────────────────────
# The Splunk HEC and Vault Transit connectors need only httpx, which is already
# a runtime dependency; the Parquet exporter is the one that pulls a new wheel.
lakehouse = ["pyarrow>=16.0.0"]
# ── Signing / secrets ────────────────────────────────────────────────────────
vault = ["hvac>=2.1.0"]
ldap = ["ldap3>=2.9.1"]
oidc = ["PyJWT[crypto]>=2.10.1,<3"]
# ── Provider extras (no extra Python deps needed — all HTTP-based) ───────────
# Install the relevant SDK for SDK-based integration (optional convenience).
anthropic-sdk = ["anthropic>=0.25.0"]
google-sdk    = ["google-generativeai>=0.7.0"]
# ── ML / inference extras ────────────────────────────────────────────────────
gpu   = ["torch>=2.0.0"]
vllm  = ["vllm>=0.4.0", "torch>=2.0.0"]
hf    = ["transformers>=4.40.0", "torch>=2.0.0"]
# ── Observability ────────────────────────────────────────────────────────────
metrics = ["prometheus-fastapi-instrumentator>=6.1.0", "prometheus-client>=0.20.0"]
otel = [
    "opentelemetry-sdk>=1.24.0",
    "opentelemetry-exporter-otlp-proto-grpc>=1.24.0",
]
# ── Post-quantum cryptography ─────────────────────────────────────────────────
# `oqs-python` was declared here and imported by nothing in this tree, so the
# extra installed an unused liboqs binding while leaving the backend the code
# actually imports undeclared. Removed rather than kept: an unimported
# dependency in a security-relevant extra is supply-chain surface for no
# capability.
pqc = ["kyber-py>=1.2.0"]
# ── Rate limiter ─────────────────────────────────────────────────────────────
ratelimit = ["cachetools>=5.3.0"]
# ── Development ──────────────────────────────────────────────────────────────
dev = [
    "pytest>=9.0.3",
    "pytest-asyncio>=0.23",
    "pytest-cov>=5.0",
    "pytest-httpserver>=1.0",
    # Parallel test execution. The suite is ~6,700 tests; `-n auto` takes the
    # wall clock from ~119 s to ~48 s, which is the difference between a gate a
    # contributor runs before pushing and one they skip.
    "pytest-xdist>=3.6",
    "kyber-py>=1.2.0",
    "hypothesis>=6.100",
    "httpx>=0.27.0",
    "ruff>=0.4",
    "mypy>=1.10",
    "bandit>=1.7",
    "pip-audit>=2.10,<3",
    "aiosqlite>=0.20.0",
    "cachetools>=5.3.0",
    "ldap3>=2.9.1",
    "PyJWT[crypto]>=2.10.1,<3",
    # The Parquet exporter's tests module-skip on this import (see
    # tests/test_optional_backend_declarations.py). Declared here as well as in
    # the lakehouse extra so the suite that CI runs actually executes them,
    # rather than reporting the connector green while never importing it.
    "pyarrow>=16.0.0",
    # Same rule, and the same reason: `prometheus-client` gates `pytest.skip`
    # calls in tests/test_observability.py, both tests/security/test_*_metric.py
    # and tests/compat/test_public_api_compat.py — including the end-to-end
    # registry test CLM-102's evidence cell cites as its LOCALLY TESTED proof.
    # The `metrics` extra is installed by no job, so every one of those skipped.
    "prometheus-client>=0.20.0",
    # The SDK's client tests (sdk/python/tests) import the provider SDKs
    # directly, so a dev environment without them fails collection instead of
    # exercising the adapter. Declared here for the same reason as pyarrow
    # above: the gate must run the code, not report it green while skipping it.
    # The SDK package itself is installed with `pip install -e sdk/python`.
    "openai>=3.35",
    "anthropic>=1.17",
    "llama-index-core>=0.14",
]
# ── Full install (all optional runtime extras) ───────────────────────────────
# `dev` is deliberately absent (REG-D74): `[all]` used to pull pytest, ruff,
# mypy, bandit, pip-audit and hypothesis into a runtime install. Contributors
# install `.[dev]` explicitly, as CI does.
all = [
    "aegis-latent-core[storage-all,vault,oidc,gpu,metrics,otel,pqc]",
]

[project.scripts]
aegis = "aegis.proxy.app:main"
aegis-server = "aegis.proxy.app:main"

[project.urls]
Homepage = "https://github.com/JuanLunaIA/aegis-latent-core"
Documentation = "https://github.com/JuanLunaIA/aegis-latent-core#readme"
Repository = "https://github.com/JuanLunaIA/aegis-latent-core"
"Bug Tracker" = "https://github.com/JuanLunaIA/aegis-latent-core/issues"
Changelog = "https://github.com/JuanLunaIA/aegis-latent-core/blob/main/CHANGELOG.md"

[tool.hatch.build.targets.wheel]
packages = ["aegis", "aegis_server", "integrations"]

[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
addopts = ""
markers = [
    "slow: marks tests as slow-running (e.g. real network / subprocess calls)",
]
filterwarnings = [
    # Third-party transition warnings. Keep message/category/module exact so
    # unrelated deprecations remain visible and fail future -W error gates.
    "ignore:^Using `httpx` with `starlette\\.testclient` is deprecated; install `httpx2` instead\\.$:starlette.exceptions.StarletteDeprecationWarning:fastapi.testclient",
    "ignore:^tagMap is deprecated\\. Please use TAG_MAP instead\\.$:DeprecationWarning:ldap3.utils.asn1",
    "ignore:^typeMap is deprecated\\. Please use TYPE_MAP instead\\.$:DeprecationWarning:ldap3.utils.asn1",
]

[tool.ruff]
line-length = 100
target-version = "py311"

# AUD-18 / REG-D22: one canonical formatter scope for the Makefile and CI.
# Both now run over the whole tree, so the two invocations cannot drift; the
# markdown exclusion keeps *linting* of Python fences inside documentation
# (which `ruff check` still covers) separate from *reformatting* the host
# document, which would rewrite 9 doc/README files whose content the doc gates
# verify and which this defect never asked for.
[tool.ruff.format]
exclude = ["*.md"]

[tool.ruff.lint]
select = ["E", "F", "I", "N", "W", "UP", "S", "B", "A", "COM", "C4", "PT"]
ignore = ["S101", "COM812", "E501", "S104", "N801", "N806", "S607", "S110"]

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["PT009", "PT011", "PT012", "PT017", "PT027", "S101", "S108", "N802", "C408", "E701", "S106", "S105", "N805", "B017", "F841", "N818", "S311", "C416"]
"aegis/core/audit_node_pb2.py" = ["E712", "E501"]
"aegis/core/**" = ["S311", "S603", "S108", "B904", "B007", "B006", "F841", "N813", "E701"]
"aegis_server/**" = ["S311"]

[tool.mypy]
python_version = "3.11"
strict = true
ignore_missing_imports = true

# Modules that soft-import an optional dependency inside try/except and fall
# back when it is absent: the native `aegis_rust` extension (built only when
# maturin runs), `torch`, `httpx`, `cachetools`, `elftools` and `Kyber1024`.
#
# Their `# type: ignore[import]` comments are load-bearing when the dependency
# is missing and redundant when it is installed, so `warn_unused_ignores`
# reports them in exactly one of the two environments. Suppressing that one
# check here keeps `mypy --strict` environment-independent; every other strict
# check, this file's own annotations included, still applies.
[[tool.mypy.overrides]]
module = [
    "aegis.core.cfi_manager",
    "aegis.core.crypto_audit",
    "aegis.core.mlkem_session",
    "aegis.core.mmr",
    "aegis.core.pqc_signer",
    "aegis.core.ratelimiter",
    "aegis.core.rust_integration",
    "aegis.core.telemetry",
    "aegis.core.ti_sharing",
    "aegis.proxy.app",
]
warn_unused_ignores = false

[tool.bandit]
exclude_dirs = ["tests"]
skips = ["B101", "B104", "B108"]

[tool.coverage.run]
source_pkgs = ["aegis"]
relative_files = true
omit = [
    "tests/*",
    # Roadmap / platform-specific modules (not required for proxy runtime).
    # This list is cross-checked by scripts/verify_import_reachability.py
    # against the real entrypoint import graph: an entry that has become
    # reachable is a drift finding, not a silent pass. Six entries below were
    # removed by that audit on 2026-09-15 because they are now reachable in
    # the live proxy/engine paths: adversarial_filter, blockchain_anchor,
    # entropy_analysis, sandbox_l1, taint_analysis, xdp_dynamic_segmentation.
    # sandbox_l2.py, also removed, no longer exists in the tree.
    "aegis/core/anchoring.py",
    "aegis/core/audit_node_pb2.py",
    "aegis/core/boot_attestation.py",
    "aegis/core/boot_guard.py",
    "aegis/core/build_hardener.py",
    "aegis/core/build_reproducibility.py",
    "aegis/core/cfi_manager.py",
    "aegis/core/codeql_config.py",
    "aegis/core/dependency_audit.py",
    "aegis/core/dpdk_engine.py",
    "aegis/core/ebpf_monitor.py",
    "aegis/core/enclave_provider.py",
    "aegis/core/forensic_sealing.py",
    "aegis/core/formal_proofs.py",
    "aegis/core/formal_specs.py",
    "aegis/core/identity.py",
    "aegis/core/kernel_hardener.py",
    "aegis/core/memory.py",
    "aegis/core/memory_invariants.py",
    "aegis/core/mte_guard.py",
    "aegis/core/network_isolation.py",
    "aegis/core/panic_mode.py",
    "aegis/core/red_team_framework.py",
    "aegis/core/root_ca_gateway.py",
    "aegis/core/sandbox.py",
    "aegis/core/secure_runtime.py",
    "aegis/core/semantic_defense.py",
    "aegis/core/state_snapshotter.py",
    "aegis/core/tpm.py",
    "aegis/core/transparency_log.py",
    "aegis/core/transport_hardener.py",
    "aegis/core/tsa_provider.py",
    "aegis/core/worm_storage.py",
    "aegis/proxy/mtls.py",
]

[tool.coverage.report]
# `--cov-fail-under` is decided with this precision, and coverage.py's default is
# 0. At 0 the gate's message and its enforcement disagree inside a half-point
# band below the floor: a run at 89.87% against --cov-fail-under=90 prints
# "FAIL Required test coverage of 90% not reached" and the process still exits 0,
# because should_fail_under rounds the total up to 90 before comparing. Measured
# against coverage 7.x: should_fail_under(89.87, 90, 0) is False while
# (89.4, 90, 0) is True; at precision 2 the whole band fails, which is what the
# printed verdict already said. This applies to every invocation -- the Makefile
# target and the CI step both pass --cov-fail-under=65 without a precision flag.
precision = 2
exclude_lines = [
    "pragma: no cover",
    "if TYPE_CHECKING:",
    "raise NotImplementedError",
    "@abstractmethod",
]