[build-system] requires = ["setuptools>=68.0", "wheel"] build-backend = "setuptools.build_meta" [project] name = "attestix" version = "0.4.1" description = "Attestix - Attestation Infrastructure for AI Agents. DID-based agent identity, W3C Verifiable Credentials, EU AI Act compliance, delegation chains, and reputation scoring. 47 MCP tools across 9 modules." readme = "README.md" license = {file = "LICENSE"} requires-python = ">=3.10" authors = [ {name = "VIBETENSOR PRIVATE LIMITED", email = "info@vibetensor.com"}, ] keywords = [ "attestix", "ai-agents", "agent-identity", "attestation", "verifiable-credentials", "verifiable-presentations", "eu-ai-act", "mcp", "mcp-server", "did", "ucan", "compliance", "ed25519", "blockchain", "gdpr", "reputation", "w3c", ] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Security :: Cryptography", "Topic :: Software Development :: Libraries :: Python Modules", ] dependencies = [ # Runtime dependencies are pinned with both lower and upper bounds to # prevent silent breakage from major-version drift (mcp 2.x, PyJWT 3.x, # cryptography 47.x, etc.) on fresh `pip install attestix`. See # paper/internal/dependency-audit-v0.3.0-2026-04-17.md for the rationale. # mcp >=1.28.1 closes the 1.27.x / <1.28.1 advisories flagged by Dependabot. "mcp[cli]>=1.28.1,<2.0.0", # cryptography >=48.0.1 closes GHSA-537c-gmf6-5ccf (in addition to the # earlier CVE-2026-34073 name-constraints bypass and CVE-2026-39892 # Hash.update buffer overflow closed in 46.0.7). # cryptography >=50.0.0 closes the 44.x-49.x advisory flagged by Dependabot. "cryptography>=50.0.0,<51.0.0", # PyJWT >=2.12.0 closes CVE-2026-32597 (crit header validation bypass). # This directly impacts Attestix's JWT / Verifiable Credential / UCAN # verification paths and is the hard blocker for the v0.3.0 release. "PyJWT[crypto]>=2.12.0,<3.0.0", "base58>=2.1.1,<3.0.0", "httpx>=0.28.0,<0.30.0", "python-dotenv>=1.1.0,<2.0.0", "nest-asyncio>=1.6.0,<2.0.0", # python-json-logger 4.x is already pulled transitively in our env; keep # the upper bound generous so we do not break existing installs. "python-json-logger>=3.3.0,<5.0.0", "filelock>=3.13.0,<4.0.0", "click>=8.1.0,<9.0.0", # Transitive pin: mcp pulls python-multipart. Versions <0.0.26 are # vulnerable to CVE-2026-40347 (DoS via large multipart preamble/epilogue) # which is exposed through both the MCP stdio server and the FastAPI # REST API's multipart uploads. Forcing >=0.0.26 here ensures the fix # lands regardless of what mcp's own floor currently is. "python-multipart>=0.0.26,<0.1.0", ] [project.optional-dependencies] # Transitive pin: web3 pulls aiohttp. Versions <=3.14.2 carry the two # advisories flagged by Dependabot, so force the floor here. blockchain = ["web3>=7.0.0,<8.0.0", "aiohttp>=3.14.3,<4.0.0"] reports = ["weasyprint>=62.0"] # Post-quantum signing (FIPS 204 ML-DSA-65 + hybrid Ed25519+ML-DSA). Optional, # never required: the default install stays classical Ed25519. dilithium-py is # pure-Python (no native build) so the verifier stays portable. See # attestix.auth.pqc. pqc = ["dilithium-py>=1.0.0,<2.0.0"] # v0.4.0 extensibility extras. The default install stays file-storage + # in-process Ed25519 signer with no external services (constitution: "Optional, # never required"). These extras are only needed for the non-default Repository / # Signer backends. pg = ["psycopg[binary]>=3.1,<4.0"] kms = ["boto3>=1.34,<2.0"] # v0.4.0-rc.3: the REST API depends on FastAPI + uvicorn. Kept as an opt-in # extra (not a base runtime dep) so callers who only need the MCP server or # Python services do not get FastAPI dragged in. The published quickstart at # attestix.io/docs/quickstart/enterprise-architect now installs `attestix[api]`. # starlette >=1.3.1 closes the five starlette advisories flagged by Dependabot # in requirements-runtime.txt; fastapi <0.130 pinned starlette 0.52.x. api = ["fastapi>=0.130,<0.140", "uvicorn[standard]>=0.32,<0.40", "starlette>=1.3.1,<2.0"] # v0.4.0-rc.3: framework integration extras. The integration callback / # adapter classes live under `attestix.integrations.` and import # their respective SDK lazily so `import attestix.integrations` itself is # always cheap. Install only the extra you need. langchain = ["langchain-core>=0.3,<0.5"] crewai = ["crewai>=0.95,<0.200"] openai-agents = ["openai-agents>=0.0.20"] test = ["pytest>=8.0", "pytest-asyncio>=0.24", "respx>=0.22", "pytest-cov>=5.0"] lint = ["ruff>=0.6.0", "mypy>=1.11"] security = ["pip-audit>=2.7", "bandit>=1.7", "safety>=3.2"] # CycloneDX SBOM generation. Produces a CycloneDX 1.5 (or later) JSON BOM # with runtime and transitive dependencies. Used in the release workflow to # attach an authoritative SBOM to every GitHub release. sbom = ["cyclonedx-bom>=5.0.0,<6.0.0"] dev = [ "pytest>=8.0", "pytest-asyncio>=0.24", "respx>=0.22", "pytest-cov>=5.0", "ruff>=0.6.0", "mypy>=1.11", "pip-audit>=2.7", "bandit>=1.7", "cyclonedx-bom>=5.0.0,<6.0.0", "build>=1.2", ] [project.urls] Homepage = "https://github.com/VibeTensor/attestix" Documentation = "https://attestix.io/docs" Repository = "https://github.com/VibeTensor/attestix" Issues = "https://github.com/VibeTensor/attestix/issues" [project.scripts] attestix = "attestix.cli:cli" [tool.setuptools] # v0.4.0-rc.2 packaging fix: the wheel ships ONLY the canonical `attestix.*` # namespace + thin deprecation shims for the pre-rc.2 flat layout. The shims # emit a DeprecationWarning on first import and are scheduled for removal in # v0.5.0. Inspect the wheel with:: # # python -c "import zipfile, sys; z=zipfile.ZipFile(sys.argv[1]); \ # print(sorted({n.split('/')[0] for n in z.namelist()}))" dist/attestix-*.whl # # Expected top-level set: {attestix, api, auth, audit, blockchain, idempotency, # services, signing, storage, tenancy, tools, attestix-.dist-info}. The # non-`attestix` entries are deprecation shims, NOT duplicate source code. packages = [ # Canonical namespace (recommended: `from attestix.services... import ...`). "attestix", "attestix.api", "attestix.api.routers", "attestix.audit", "attestix.auth", "attestix.blockchain", "attestix.idempotency", # v0.4.0-rc.3: framework integration adapters. Each subpackage lazily # imports its framework so the parent `attestix.integrations` is always # cheap to import. P0 #1 of the rc.2 validation: the wheel previously # shipped no `attestix.integrations.*` at all, so the indie-dev # quickstart's `from attestix.integrations.langchain import # AttestixCallback` raised ModuleNotFoundError on every fresh install. "attestix.integrations", "attestix.integrations.langchain", "attestix.integrations.openai_agents", "attestix.integrations.crewai", "attestix.services", "attestix.signing", "attestix.storage", "attestix.tenancy", "attestix.tools", # Deprecation shims for the pre-rc.2 flat layout (emit DeprecationWarning, # scheduled for removal in v0.5.0). Each subpackage is a tiny re-export of # the canonical `attestix.` namespace. "api", "api.routers", "audit", "auth", "blockchain", "idempotency", "services", "signing", "storage", "tenancy", "tools", ] # Top-level deprecation shims for the pre-rc.2 flat layout. These delegate to # `attestix.` and emit a DeprecationWarning on import. py-modules = ["cli", "config", "errors", "main"] [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"] # Disable the Pydantic logfire plugin by default. When CrewAI (or other deps) # pull in an opentelemetry-sdk version that is incompatible with the installed # logfire, the plugin raises ImportError at collection time and aborts the # whole suite. We do not need logfire for our tests. addopts = "-p no:logfire" markers = [ "live_blockchain: requires funded Base Sepolia wallet (deselect with -m 'not live_blockchain')", "perf: performance guard tests (issuance scaling); generous CI thresholds (deselect with -m 'not perf')", ] [tool.ruff] line-length = 120 target-version = "py310" extend-exclude = [ "build", "dist", ".venv", "site", "magicuidesign-agent-template", "magicuidesign-devtool-template", "website", "attestix.egg-info", ] [tool.ruff.lint] # Keep initial rule set conservative so CI passes out of the box; expand later # with `ruff check --fix` in a dedicated cleanup PR. select = ["E", "F", "W"] ignore = [ "E501", # line-length handled by formatter "E402", # module level import not at top of file (dynamic path tweaks) "E701", # multiple statements on one line (colon) "E702", # multiple statements on one line (semicolon) "E722", # bare except (legacy code has some) "E741", # ambiguous variable name "F401", # unused imports (re-exports in __init__ files) "F403", # star imports "F405", # may be undefined from star imports "F541", # f-string without placeholders "F811", # redefinition "F841", # local variable assigned but not used ] [tool.ruff.lint.per-file-ignores] "tests/**" = ["F401", "F811", "E402", "E401"] "examples/**" = ["F401", "E402"] "demo/**" = ["F401", "E402", "E401"] [tool.mypy] python_version = "3.10" ignore_missing_imports = true follow_imports = "silent" warn_unused_ignores = false warn_redundant_casts = true # Keep mypy permissive initially; tighten over time. check_untyped_defs = false disallow_untyped_defs = false exclude = [ "build/", "dist/", "site/", "website/", "magicuidesign-agent-template/", "magicuidesign-devtool-template/", "tests/", "examples/", "demo/", "paper/", ] [tool.bandit] exclude_dirs = [ "tests", "build", "dist", "site", "website", "magicuidesign-agent-template", "magicuidesign-devtool-template", "examples", "demo", ".venv", ] skips = ["B101", "B311", "B404", "B603", "B607"] [tool.coverage.run] source = ["attestix"] branch = true omit = [ "tests/*", "examples/*", "demo/*", "build/*", "dist/*", "*/__init__.py", ] [tool.coverage.report] show_missing = true skip_covered = false exclude_lines = [ "pragma: no cover", "if __name__ == .__main__.:", "raise NotImplementedError", "if TYPE_CHECKING:", ] [tool.setuptools.package-data] # Only ship Python source files, never JSON data "attestix.blockchain" = ["*.py"] [tool.setuptools.exclude-package-data] # Runtime data files are generated on first use - never ship them "*" = [ "identities.json", "compliance.json", "credentials.json", "delegations.json", "reputation.json", "provenance.json", "anchors.json", "*.json.bak", "*.json.tmp", "*.json.lock", ".signing_key.json", ".keypairs.json", ".blockchain_config.json", "*.log", ]