sentinelworks 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sentinelworks-0.1.0/.gitignore +48 -0
- sentinelworks-0.1.0/NOTICE +3 -0
- sentinelworks-0.1.0/PKG-INFO +22 -0
- sentinelworks-0.1.0/docs/ask-resume.md +25 -0
- sentinelworks-0.1.0/docs/compat.md +38 -0
- sentinelworks-0.1.0/docs/credentials.md +39 -0
- sentinelworks-0.1.0/docs/langchain.md +127 -0
- sentinelworks-0.1.0/docs/shutdown.md +34 -0
- sentinelworks-0.1.0/pyproject.toml +45 -0
- sentinelworks-0.1.0/src/sentinelworks/__about__.py +1 -0
- sentinelworks-0.1.0/src/sentinelworks/__init__.py +7 -0
- sentinelworks-0.1.0/src/sentinelworks/_b64.py +26 -0
- sentinelworks-0.1.0/src/sentinelworks/_claims.py +46 -0
- sentinelworks-0.1.0/src/sentinelworks/_clock.py +49 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/__init__.py +0 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/agent_event.py +341 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/__init__.py +2 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/defaults.py +260 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/domains.py +29 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/environments.py +41 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/evidence.py +25 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/fact_manifest.py +46 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/intel.py +66 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/issuers.py +110 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/keyset.py +59 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/manifest.py +153 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/mcp_trust.py +35 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/params.py +197 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/profile.py +82 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/redaction.py +93 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/risk.py +35 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/rules.py +72 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/sync_keys.py +68 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/bundle/tools.py +104 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/capsule.py +150 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/decision_record.py +1128 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/policy/__init__.py +2 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/policy/input_v1.py +302 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/policy/output_v1.py +165 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/policy/rule_metadata_v1.py +78 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/startup_manifest.py +287 -0
- sentinelworks-0.1.0/src/sentinelworks/_gen/sync_state.py +127 -0
- sentinelworks-0.1.0/src/sentinelworks/_ids.py +39 -0
- sentinelworks-0.1.0/src/sentinelworks/_jcs.py +96 -0
- sentinelworks-0.1.0/src/sentinelworks/_log.py +47 -0
- sentinelworks-0.1.0/src/sentinelworks/_wire.py +76 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/__init__.py +1 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/_common/__init__.py +1 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/_common/apply.py +44 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/_common/ask.py +55 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/_common/capsule.py +26 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/_common/counters.py +16 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/_common/declare.py +80 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/_common/events.py +163 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/_common/refusal.py +15 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/_common/threads.py +174 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/_common/tools.py +47 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/capability.py +53 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/crewai/__init__.py +7 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/crewai/_compat.py +220 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/crewai/declarations/1.0.json +64 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/crewai/declarations/1.0.yaml +14 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/crewai/flow_state.py +32 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/crewai/hooks_kickoff.py +104 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/crewai/hooks_llm.py +79 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/crewai/hooks_tool.py +100 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/crewai/install.py +130 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/crewai/manifest.py +135 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/crewai/messages.py +95 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/crewai/runs.py +290 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/crewai/tools.py +96 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/__init__.py +8 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/_compat.py +126 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/context.py +64 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/core.py +164 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/declarations/1.0.json +70 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/declarations/1.0.yaml +14 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/flows_input.py +79 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/flows_model.py +157 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/flows_output.py +50 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/flows_tool.py +194 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/manifest.py +96 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/messages.py +112 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/middleware.py +180 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/state.py +60 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/stream.py +194 -0
- sentinelworks-0.1.0/src/sentinelworks/adapters/langchain/tools.py +122 -0
- sentinelworks-0.1.0/src/sentinelworks/api.py +170 -0
- sentinelworks-0.1.0/src/sentinelworks/assets/engine_calibration.json +10 -0
- sentinelworks-0.1.0/src/sentinelworks/assets/root_keys.json +19 -0
- sentinelworks-0.1.0/src/sentinelworks/assets/safemode.wasm +0 -0
- sentinelworks-0.1.0/src/sentinelworks/assets.py +35 -0
- sentinelworks-0.1.0/src/sentinelworks/bootstrap.py +185 -0
- sentinelworks-0.1.0/src/sentinelworks/bundle/__init__.py +1 -0
- sentinelworks-0.1.0/src/sentinelworks/bundle/cache.py +151 -0
- sentinelworks-0.1.0/src/sentinelworks/bundle/delta.py +21 -0
- sentinelworks-0.1.0/src/sentinelworks/bundle/floors.py +20 -0
- sentinelworks-0.1.0/src/sentinelworks/bundle/indexes.py +64 -0
- sentinelworks-0.1.0/src/sentinelworks/bundle/jws.py +86 -0
- sentinelworks-0.1.0/src/sentinelworks/bundle/keys.py +73 -0
- sentinelworks-0.1.0/src/sentinelworks/bundle/loader.py +259 -0
- sentinelworks-0.1.0/src/sentinelworks/bundle/manager.py +104 -0
- sentinelworks-0.1.0/src/sentinelworks/bundle/manifest.py +77 -0
- sentinelworks-0.1.0/src/sentinelworks/bundle/snapshot.py +213 -0
- sentinelworks-0.1.0/src/sentinelworks/bundle/unpack.py +96 -0
- sentinelworks-0.1.0/src/sentinelworks/config.py +130 -0
- sentinelworks-0.1.0/src/sentinelworks/crewai.py +7 -0
- sentinelworks-0.1.0/src/sentinelworks/engine/__init__.py +1 -0
- sentinelworks-0.1.0/src/sentinelworks/engine/base.py +51 -0
- sentinelworks-0.1.0/src/sentinelworks/engine/calibration.py +29 -0
- sentinelworks-0.1.0/src/sentinelworks/engine/epoch.py +31 -0
- sentinelworks-0.1.0/src/sentinelworks/engine/opa_wasm.py +391 -0
- sentinelworks-0.1.0/src/sentinelworks/engine/pool.py +134 -0
- sentinelworks-0.1.0/src/sentinelworks/engine/safemode_engine.py +11 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/__init__.py +83 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/_access.py +97 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/_jwt.py +137 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/_psl_data.py +10422 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/_url.py +106 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/approver.py +49 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/dest_extract.py +156 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/dest_index.py +90 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/e10_ledger.py +61 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/e13_intel.py +113 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/e14_risk.py +64 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/e18_facts.py +176 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/e1_delta.py +118 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/e2_class.py +38 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/e4_scan.py +269 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/e5_dest.py +102 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/e7_env.py +80 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/e8_mcp.py +54 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/env_index.py +120 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/intel_index.py +218 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/intel_norm.py +136 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/issuers_index.py +110 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/mcp_index.py +43 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/pins.py +107 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/policy_args.py +45 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/principal.py +108 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/psl.py +64 -0
- sentinelworks-0.1.0/src/sentinelworks/enrich/risk_index.py +65 -0
- sentinelworks-0.1.0/src/sentinelworks/errors.py +66 -0
- sentinelworks-0.1.0/src/sentinelworks/langchain.py +7 -0
- sentinelworks-0.1.0/src/sentinelworks/ledger/__init__.py +14 -0
- sentinelworks-0.1.0/src/sentinelworks/ledger/capsule.py +139 -0
- sentinelworks-0.1.0/src/sentinelworks/ledger/keys.py +61 -0
- sentinelworks-0.1.0/src/sentinelworks/ledger/memory.py +365 -0
- sentinelworks-0.1.0/src/sentinelworks/ledger/null.py +59 -0
- sentinelworks-0.1.0/src/sentinelworks/ledger/port.py +70 -0
- sentinelworks-0.1.0/src/sentinelworks/ledger/state.py +67 -0
- sentinelworks-0.1.0/src/sentinelworks/ledger/taint.py +16 -0
- sentinelworks-0.1.0/src/sentinelworks/offline.py +168 -0
- sentinelworks-0.1.0/src/sentinelworks/outbox/__init__.py +49 -0
- sentinelworks-0.1.0/src/sentinelworks/outbox/backoff.py +57 -0
- sentinelworks-0.1.0/src/sentinelworks/outbox/batch.py +103 -0
- sentinelworks-0.1.0/src/sentinelworks/outbox/context.py +116 -0
- sentinelworks-0.1.0/src/sentinelworks/outbox/host.py +89 -0
- sentinelworks-0.1.0/src/sentinelworks/outbox/hybrid.py +590 -0
- sentinelworks-0.1.0/src/sentinelworks/outbox/null.py +7 -0
- sentinelworks-0.1.0/src/sentinelworks/outbox/port.py +72 -0
- sentinelworks-0.1.0/src/sentinelworks/outbox/recovery.py +122 -0
- sentinelworks-0.1.0/src/sentinelworks/outbox/segments.py +77 -0
- sentinelworks-0.1.0/src/sentinelworks/outbox/stores.py +219 -0
- sentinelworks-0.1.0/src/sentinelworks/outbox/sync_state.py +155 -0
- sentinelworks-0.1.0/src/sentinelworks/outbox/tasks.py +394 -0
- sentinelworks-0.1.0/src/sentinelworks/outbox/wire.py +40 -0
- sentinelworks-0.1.0/src/sentinelworks/py.typed +0 -0
- sentinelworks-0.1.0/src/sentinelworks/redact/__init__.py +14 -0
- sentinelworks-0.1.0/src/sentinelworks/redact/_default_profile.py +55 -0
- sentinelworks-0.1.0/src/sentinelworks/redact/_vendor.py +179 -0
- sentinelworks-0.1.0/src/sentinelworks/redact/apply.py +233 -0
- sentinelworks-0.1.0/src/sentinelworks/redact/detectors.py +198 -0
- sentinelworks-0.1.0/src/sentinelworks/redact/gate.py +279 -0
- sentinelworks-0.1.0/src/sentinelworks/redact/pointers.py +36 -0
- sentinelworks-0.1.0/src/sentinelworks/redact/port.py +69 -0
- sentinelworks-0.1.0/src/sentinelworks/redact/profile.py +66 -0
- sentinelworks-0.1.0/src/sentinelworks/redact/validators.py +59 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/__init__.py +1 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/ask.py +135 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/core.py +443 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/decide.py +791 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/events.py +123 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/facts.py +127 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/gate.py +32 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/manifest.py +328 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/modes.py +83 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/policy.py +178 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/ports.py +108 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/principal.py +71 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/record.py +241 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/safemode.py +45 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/stages.py +30 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/types.py +207 -0
- sentinelworks-0.1.0/src/sentinelworks/runtime/verdict.py +114 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/__init__.py +37 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/credentials.py +100 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/dpop.py +84 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/errors.py +70 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/grant.py +45 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/http.py +373 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/port.py +53 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/providers/__init__.py +42 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/providers/_common.py +96 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/providers/aws.py +167 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/providers/azure.py +82 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/providers/gcp.py +57 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/providers/k8s.py +24 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/providers/oidc.py +27 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/providers/spiffe.py +107 -0
- sentinelworks-0.1.0/src/sentinelworks/transport/wire_models.py +117 -0
- sentinelworks-0.1.0/tests/__init__.py +0 -0
- sentinelworks-0.1.0/tests/bench/bench_enrichers.py +106 -0
- sentinelworks-0.1.0/tests/bench/bench_gate.py +35 -0
- sentinelworks-0.1.0/tests/bench/bench_langchain_adapter.py +55 -0
- sentinelworks-0.1.0/tests/bench/bench_outbox.py +87 -0
- sentinelworks-0.1.0/tests/bench/test_bundle_load_bench.py +41 -0
- sentinelworks-0.1.0/tests/bench/test_micro.py +68 -0
- sentinelworks-0.1.0/tests/bench/test_noop_bench.py +8 -0
- sentinelworks-0.1.0/tests/capsule/__init__.py +0 -0
- sentinelworks-0.1.0/tests/capsule/conftest.py +32 -0
- sentinelworks-0.1.0/tests/capsule/test_capsule_end_to_end.py +144 -0
- sentinelworks-0.1.0/tests/conformance/__init__.py +0 -0
- sentinelworks-0.1.0/tests/conformance/ask_process.py +71 -0
- sentinelworks-0.1.0/tests/conformance/conftest.py +83 -0
- sentinelworks-0.1.0/tests/conformance/harness.py +92 -0
- sentinelworks-0.1.0/tests/conformance/harness_crewai.py +234 -0
- sentinelworks-0.1.0/tests/conformance/harness_langchain.py +165 -0
- sentinelworks-0.1.0/tests/conformance/matrix/crewai-high.txt +167 -0
- sentinelworks-0.1.0/tests/conformance/matrix/crewai-latest-minor.txt +167 -0
- sentinelworks-0.1.0/tests/conformance/matrix/crewai-low.txt +167 -0
- sentinelworks-0.1.0/tests/conformance/matrix/langchain-1.3-high.txt +40 -0
- sentinelworks-0.1.0/tests/conformance/matrix/langchain-1.3-low.txt +40 -0
- sentinelworks-0.1.0/tests/conformance/matrix/langchain-1.4-high.txt +40 -0
- sentinelworks-0.1.0/tests/conformance/matrix/langchain-1.4-low.txt +40 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/__init__.py +0 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/c01_principal_worker_threads.py +31 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/c02_two_concurrent_kickoffs.py +40 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/c03_delegation_stack_cleanup.py +55 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/c04_crew_inside_flow.py +40 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/c05_capsule_modes.py +25 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/c06_ask_absent_resolves_deny.py +32 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/c07_uninstrumented_crew.py +23 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/c08_kickoff_for_each.py +37 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s01_points.py +45 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s02_allow.py +38 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s03_modify_prompt.py +22 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s04_modify_args.py +32 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s05_disclaimer.py +18 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s06_deny.py +67 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s07_ask.py +54 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s08_parallel_tools.py +33 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s09_tampered.py +23 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s10_deleted.py +23 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s11_delegation.py +37 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s12_stream.py +34 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s13_manifest.py +56 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s14_status_gate.py +71 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s15_safe_mode.py +36 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s16_principal.py +44 -0
- sentinelworks-0.1.0/tests/conformance/scenarios/s17_end_run.py +36 -0
- sentinelworks-0.1.0/tests/conformance/test_same_decisions.py +49 -0
- sentinelworks-0.1.0/tests/conformance/test_scenarios.py +45 -0
- sentinelworks-0.1.0/tests/conformance/v04/__init__.py +0 -0
- sentinelworks-0.1.0/tests/conformance/v04/test_v04_checks.py +358 -0
- sentinelworks-0.1.0/tests/conformance/v05/__init__.py +0 -0
- sentinelworks-0.1.0/tests/conformance/v05/test_v05_checks.py +194 -0
- sentinelworks-0.1.0/tests/conformance/v06/__init__.py +0 -0
- sentinelworks-0.1.0/tests/conformance/v06/flow_process.py +66 -0
- sentinelworks-0.1.0/tests/conformance/v06/test_v06_checks.py +296 -0
- sentinelworks-0.1.0/tests/conftest.py +31 -0
- sentinelworks-0.1.0/tests/contracts/.gitkeep +0 -0
- sentinelworks-0.1.0/tests/contracts/test_ask_id_rule.py +27 -0
- sentinelworks-0.1.0/tests/contracts/test_canary.py +90 -0
- sentinelworks-0.1.0/tests/contracts/test_capsule_rule.py +70 -0
- sentinelworks-0.1.0/tests/contracts/test_intel_match_vectors.py +56 -0
- sentinelworks-0.1.0/tests/contracts/test_learned_vectors_engine.py +100 -0
- sentinelworks-0.1.0/tests/enrich/__init__.py +0 -0
- sentinelworks-0.1.0/tests/enrich/jwt_support.py +73 -0
- sentinelworks-0.1.0/tests/enrich/test_approver.py +50 -0
- sentinelworks-0.1.0/tests/enrich/test_e10_ledger.py +68 -0
- sentinelworks-0.1.0/tests/enrich/test_e18_facts.py +240 -0
- sentinelworks-0.1.0/tests/enrich/test_e1_delta.py +92 -0
- sentinelworks-0.1.0/tests/enrich/test_e4_scan.py +210 -0
- sentinelworks-0.1.0/tests/enrich/test_principal.py +215 -0
- sentinelworks-0.1.0/tests/enrich/test_provides_cover.py +35 -0
- sentinelworks-0.1.0/tests/fixtures/intel/slice_bad_dates.json +71 -0
- sentinelworks-0.1.0/tests/fixtures/intel/slice_expired.json +71 -0
- sentinelworks-0.1.0/tests/fixtures/intel/slice_small.json +71 -0
- sentinelworks-0.1.0/tests/fixtures/policy/alloc.rego +5 -0
- sentinelworks-0.1.0/tests/fixtures/policy/builtin.rego +5 -0
- sentinelworks-0.1.0/tests/fixtures/policy/echo.rego +7 -0
- sentinelworks-0.1.0/tests/fixtures/policy/loop.rego +13 -0
- sentinelworks-0.1.0/tests/fixtures/policy/params_overlay.rego +5 -0
- sentinelworks-0.1.0/tests/fixtures/policy/table.rego +5 -0
- sentinelworks-0.1.0/tests/fixtures/risk/elevated.json +9 -0
- sentinelworks-0.1.0/tests/fixtures/risk/expired.json +9 -0
- sentinelworks-0.1.0/tests/fixtures/risk/high.json +10 -0
- sentinelworks-0.1.0/tests/fixtures/risk/normal.json +7 -0
- sentinelworks-0.1.0/tests/fixtures/risk/unknown_level.json +9 -0
- sentinelworks-0.1.0/tests/integration/.gitkeep +0 -0
- sentinelworks-0.1.0/tests/integration/__init__.py +0 -0
- sentinelworks-0.1.0/tests/integration/test_credentials_fakecell.py +191 -0
- sentinelworks-0.1.0/tests/integration/test_crewai_e2e.py +75 -0
- sentinelworks-0.1.0/tests/integration/test_e13_e14_fakecell.py +154 -0
- sentinelworks-0.1.0/tests/integration/test_langchain_e2e.py +71 -0
- sentinelworks-0.1.0/tests/integration/test_langchain_postgres_saver.py +63 -0
- sentinelworks-0.1.0/tests/integration/test_spiffe_real_package.py +34 -0
- sentinelworks-0.1.0/tests/integration/test_status_end_to_end.py +131 -0
- sentinelworks-0.1.0/tests/ledger/__init__.py +0 -0
- sentinelworks-0.1.0/tests/ledger/support.py +33 -0
- sentinelworks-0.1.0/tests/ledger/test_ledger_contract.py +275 -0
- sentinelworks-0.1.0/tests/ledger/test_memory_ledger.py +106 -0
- sentinelworks-0.1.0/tests/ledger/test_taint_rule.py +22 -0
- sentinelworks-0.1.0/tests/outbox/__init__.py +0 -0
- sentinelworks-0.1.0/tests/outbox/support.py +26 -0
- sentinelworks-0.1.0/tests/outbox/test_disk_errors.py +249 -0
- sentinelworks-0.1.0/tests/outbox/test_fork.py +72 -0
- sentinelworks-0.1.0/tests/outbox/test_hybrid_append.py +118 -0
- sentinelworks-0.1.0/tests/outbox/test_pressure.py +137 -0
- sentinelworks-0.1.0/tests/outbox/test_recovery.py +215 -0
- sentinelworks-0.1.0/tests/outbox/test_segments.py +131 -0
- sentinelworks-0.1.0/tests/redact/__init__.py +0 -0
- sentinelworks-0.1.0/tests/redact/test_apply_caps.py +101 -0
- sentinelworks-0.1.0/tests/redact/test_detectors_vectors.py +86 -0
- sentinelworks-0.1.0/tests/redact/test_gate_stages.py +227 -0
- sentinelworks-0.1.0/tests/redact/test_keyed_hash_vectors.py +46 -0
- sentinelworks-0.1.0/tests/redact/test_overlap.py +22 -0
- sentinelworks-0.1.0/tests/redact/test_pointer_keys.py +40 -0
- sentinelworks-0.1.0/tests/redact/test_profile_merge.py +38 -0
- sentinelworks-0.1.0/tests/redact/test_validators.py +31 -0
- sentinelworks-0.1.0/tests/redact/test_vendor_assets.py +59 -0
- sentinelworks-0.1.0/tests/redact/test_views.py +46 -0
- sentinelworks-0.1.0/tests/support/__init__.py +0 -0
- sentinelworks-0.1.0/tests/support/agents.py +74 -0
- sentinelworks-0.1.0/tests/support/approvers.py +29 -0
- sentinelworks-0.1.0/tests/support/aws_verifier.py +61 -0
- sentinelworks-0.1.0/tests/support/bundle_builder.py +241 -0
- sentinelworks-0.1.0/tests/support/crewai_fake.py +73 -0
- sentinelworks-0.1.0/tests/support/enrich.py +51 -0
- sentinelworks-0.1.0/tests/support/factories.py +192 -0
- sentinelworks-0.1.0/tests/support/fake_cell.py +634 -0
- sentinelworks-0.1.0/tests/support/fake_metadata.py +62 -0
- sentinelworks-0.1.0/tests/support/fake_ports.py +173 -0
- sentinelworks-0.1.0/tests/support/fake_workload_api.py +33 -0
- sentinelworks-0.1.0/tests/support/models.py +89 -0
- sentinelworks-0.1.0/tests/support/pipeline.py +144 -0
- sentinelworks-0.1.0/tests/support/scripted_engine.py +56 -0
- sentinelworks-0.1.0/tests/support/shutdown_worker.py +135 -0
- sentinelworks-0.1.0/tests/support/vectors.py +33 -0
- sentinelworks-0.1.0/tests/support/wasm.py +28 -0
- sentinelworks-0.1.0/tests/sync/__init__.py +0 -0
- sentinelworks-0.1.0/tests/sync/support.py +89 -0
- sentinelworks-0.1.0/tests/sync/test_drain.py +174 -0
- sentinelworks-0.1.0/tests/sync/test_http_error_table.py +283 -0
- sentinelworks-0.1.0/tests/sync/test_reenroll.py +79 -0
- sentinelworks-0.1.0/tests/sync/test_sync_state_vectors.py +157 -0
- sentinelworks-0.1.0/tests/sync/test_tasks_bundle.py +132 -0
- sentinelworks-0.1.0/tests/sync/test_tasks_renew.py +129 -0
- sentinelworks-0.1.0/tests/sync/test_tasks_upload.py +318 -0
- sentinelworks-0.1.0/tests/sync/test_wire.py +89 -0
- sentinelworks-0.1.0/tests/test_canary_crewai.py +110 -0
- sentinelworks-0.1.0/tests/test_canary_langchain.py +155 -0
- sentinelworks-0.1.0/tests/test_canary_outbox_and_upload.py +123 -0
- sentinelworks-0.1.0/tests/test_canary_pipeline.py +202 -0
- sentinelworks-0.1.0/tests/test_canary_providers.py +83 -0
- sentinelworks-0.1.0/tests/test_graceful_shutdown.py +185 -0
- sentinelworks-0.1.0/tests/test_runtime_with_real_enrichers.py +159 -0
- sentinelworks-0.1.0/tests/transport/__init__.py +0 -0
- sentinelworks-0.1.0/tests/transport/support.py +98 -0
- sentinelworks-0.1.0/tests/transport/test_credentials.py +80 -0
- sentinelworks-0.1.0/tests/transport/test_dpop_vectors.py +102 -0
- sentinelworks-0.1.0/tests/transport/test_enroll_renew.py +262 -0
- sentinelworks-0.1.0/tests/transport/test_fake_cell.py +157 -0
- sentinelworks-0.1.0/tests/transport/test_http_errors.py +125 -0
- sentinelworks-0.1.0/tests/unit/__init__.py +0 -0
- sentinelworks-0.1.0/tests/unit/adapters/__init__.py +0 -0
- sentinelworks-0.1.0/tests/unit/adapters/_common/__init__.py +0 -0
- sentinelworks-0.1.0/tests/unit/adapters/_common/test_apply.py +49 -0
- sentinelworks-0.1.0/tests/unit/adapters/_common/test_ask.py +62 -0
- sentinelworks-0.1.0/tests/unit/adapters/_common/test_capsule.py +37 -0
- sentinelworks-0.1.0/tests/unit/adapters/_common/test_declare.py +67 -0
- sentinelworks-0.1.0/tests/unit/adapters/_common/test_events.py +169 -0
- sentinelworks-0.1.0/tests/unit/adapters/_common/test_refusal.py +25 -0
- sentinelworks-0.1.0/tests/unit/adapters/_common/test_threads.py +128 -0
- sentinelworks-0.1.0/tests/unit/adapters/_common/test_tools.py +46 -0
- sentinelworks-0.1.0/tests/unit/adapters/crewai/__init__.py +0 -0
- sentinelworks-0.1.0/tests/unit/adapters/crewai/test_fail_closed.py +102 -0
- sentinelworks-0.1.0/tests/unit/adapters/crewai/test_install.py +117 -0
- sentinelworks-0.1.0/tests/unit/adapters/crewai/test_parts.py +152 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/__init__.py +0 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/conftest.py +23 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/test_compat_inventory.py +82 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/test_context.py +57 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/test_declaration.py +65 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/test_flows_input.py +84 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/test_flows_model.py +117 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/test_flows_output.py +98 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/test_flows_tool.py +285 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/test_manifest.py +67 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/test_messages.py +77 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/test_shim_imports.py +18 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/test_state_reducer.py +43 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/test_stream_window.py +94 -0
- sentinelworks-0.1.0/tests/unit/adapters/langchain/test_tools_detect.py +91 -0
- sentinelworks-0.1.0/tests/unit/bundle/__init__.py +0 -0
- sentinelworks-0.1.0/tests/unit/bundle/test_bundle_trust.py +337 -0
- sentinelworks-0.1.0/tests/unit/bundle/test_cache.py +89 -0
- sentinelworks-0.1.0/tests/unit/bundle/test_delta.py +106 -0
- sentinelworks-0.1.0/tests/unit/bundle/test_jws.py +88 -0
- sentinelworks-0.1.0/tests/unit/bundle/test_signal_parts.py +68 -0
- sentinelworks-0.1.0/tests/unit/bundle/test_unpack.py +99 -0
- sentinelworks-0.1.0/tests/unit/engine/__init__.py +0 -0
- sentinelworks-0.1.0/tests/unit/engine/test_engine_pool.py +243 -0
- sentinelworks-0.1.0/tests/unit/enrich/test_dest_extract.py +57 -0
- sentinelworks-0.1.0/tests/unit/enrich/test_dest_extract_paths.py +48 -0
- sentinelworks-0.1.0/tests/unit/enrich/test_dest_index.py +54 -0
- sentinelworks-0.1.0/tests/unit/enrich/test_e13_intel.py +197 -0
- sentinelworks-0.1.0/tests/unit/enrich/test_e14_risk.py +137 -0
- sentinelworks-0.1.0/tests/unit/enrich/test_e5_e7_e8.py +269 -0
- sentinelworks-0.1.0/tests/unit/enrich/test_env_index.py +56 -0
- sentinelworks-0.1.0/tests/unit/enrich/test_intel_index.py +177 -0
- sentinelworks-0.1.0/tests/unit/enrich/test_intel_norm.py +93 -0
- sentinelworks-0.1.0/tests/unit/enrich/test_mcp_index.py +30 -0
- sentinelworks-0.1.0/tests/unit/enrich/test_pin_tracker.py +83 -0
- sentinelworks-0.1.0/tests/unit/enrich/test_psl.py +97 -0
- sentinelworks-0.1.0/tests/unit/enrich/test_risk_index.py +68 -0
- sentinelworks-0.1.0/tests/unit/enrich/test_url.py +92 -0
- sentinelworks-0.1.0/tests/unit/ledger/__init__.py +0 -0
- sentinelworks-0.1.0/tests/unit/ledger/test_capsule.py +179 -0
- sentinelworks-0.1.0/tests/unit/runtime/__init__.py +0 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_api.py +115 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_ask.py +160 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_decide_flows.py +267 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_decide_vectors.py +79 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_facts.py +95 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_failclosed.py +181 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_fork.py +107 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_gate.py +39 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_lifecycle.py +324 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_manifest.py +174 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_modes.py +63 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_overlays.py +149 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_principal.py +75 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_record.py +156 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_safe_mode.py +46 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_shutdown.py +106 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_swap_under_load.py +61 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_verdict.py +131 -0
- sentinelworks-0.1.0/tests/unit/runtime/test_wire.py +67 -0
- sentinelworks-0.1.0/tests/unit/test_clock.py +20 -0
- sentinelworks-0.1.0/tests/unit/test_ids.py +36 -0
- sentinelworks-0.1.0/tests/unit/test_import.py +5 -0
- sentinelworks-0.1.0/tests/unit/test_jcs.py +107 -0
- sentinelworks-0.1.0/tests/unit/test_offline.py +196 -0
- sentinelworks-0.1.0/tests/unit/test_offline_derive.py +48 -0
- sentinelworks-0.1.0/tests/unit/test_public_api.py +99 -0
- sentinelworks-0.1.0/tests/unit/transport/__init__.py +0 -0
- sentinelworks-0.1.0/tests/unit/transport/test_credentials_autodetect.py +143 -0
- sentinelworks-0.1.0/tests/unit/transport/test_provider_common.py +48 -0
- sentinelworks-0.1.0/tests/unit/transport/test_providers.py +363 -0
- sentinelworks-0.1.0/tests/vectors/decide/allow_agent_response.json +10 -0
- sentinelworks-0.1.0/tests/vectors/decide/allow_post_llm.json +10 -0
- sentinelworks-0.1.0/tests/vectors/decide/allow_post_tool.json +10 -0
- sentinelworks-0.1.0/tests/vectors/decide/allow_pre_llm.json +10 -0
- sentinelworks-0.1.0/tests/vectors/decide/allow_pre_tool.json +10 -0
- sentinelworks-0.1.0/tests/vectors/decide/allow_prompt_submit.json +10 -0
- sentinelworks-0.1.0/tests/vectors/decide/allow_session_start.json +10 -0
- sentinelworks-0.1.0/tests/vectors/decide/deny_agent_response.json +21 -0
- sentinelworks-0.1.0/tests/vectors/decide/deny_post_llm.json +20 -0
- sentinelworks-0.1.0/tests/vectors/decide/deny_post_tool_withholds.json +21 -0
- sentinelworks-0.1.0/tests/vectors/decide/deny_pre_llm.json +20 -0
- sentinelworks-0.1.0/tests/vectors/decide/deny_pre_tool.json +20 -0
- sentinelworks-0.1.0/tests/vectors/decide/deny_prompt_submit.json +21 -0
- sentinelworks-0.1.0/tests/vectors/decide/deny_session_start.json +21 -0
- sentinelworks-0.1.0/tests/vectors/decide/end_run_obligation.json +20 -0
- sentinelworks-0.1.0/tests/vectors/decide/engine_error_non_tool.json +11 -0
- sentinelworks-0.1.0/tests/vectors/decide/engine_error_tier_critical.json +25 -0
- sentinelworks-0.1.0/tests/vectors/decide/engine_error_tier_high.json +25 -0
- sentinelworks-0.1.0/tests/vectors/decide/engine_error_tier_low.json +25 -0
- sentinelworks-0.1.0/tests/vectors/decide/engine_error_tier_medium.json +25 -0
- sentinelworks-0.1.0/tests/vectors/decide/gate_draining_pre_tool_passes.json +11 -0
- sentinelworks-0.1.0/tests/vectors/decide/gate_draining_prompt.json +14 -0
- sentinelworks-0.1.0/tests/vectors/decide/gate_restricted_post_tool_passes.json +11 -0
- sentinelworks-0.1.0/tests/vectors/decide/gate_restricted_pre_tool.json +17 -0
- sentinelworks-0.1.0/tests/vectors/decide/gate_restricted_prompt.json +15 -0
- sentinelworks-0.1.0/tests/vectors/decide/gate_revoked_post_tool_passes.json +11 -0
- sentinelworks-0.1.0/tests/vectors/decide/gate_revoked_pre_tool.json +17 -0
- sentinelworks-0.1.0/tests/vectors/decide/gate_revoked_prompt.json +15 -0
- sentinelworks-0.1.0/tests/vectors/decide/modify_agent_response.json +22 -0
- sentinelworks-0.1.0/tests/vectors/decide/modify_post_llm.json +22 -0
- sentinelworks-0.1.0/tests/vectors/decide/modify_post_tool.json +22 -0
- sentinelworks-0.1.0/tests/vectors/decide/modify_pre_llm.json +22 -0
- sentinelworks-0.1.0/tests/vectors/decide/modify_pre_tool.json +22 -0
- sentinelworks-0.1.0/tests/vectors/decide/modify_prompt_submit.json +22 -0
- sentinelworks-0.1.0/tests/vectors/decide/modify_session_start_denies.json +20 -0
- sentinelworks-0.1.0/tests/vectors/decide/pre_tool_ask_adapter_off.json +32 -0
- sentinelworks-0.1.0/tests/vectors/decide/pre_tool_ask_interrupts.json +35 -0
- sentinelworks-0.1.0/tests/vectors/decide/pre_tool_ask_not_declared.json +41 -0
- sentinelworks-0.1.0/tests/vectors/decide/pre_tool_high_no_channel.json +37 -0
- sentinelworks-0.1.0/tests/vectors/decide/safe_mode_model_allows_notifies.json +14 -0
- sentinelworks-0.1.0/tests/vectors/decide/safe_mode_tool_denies.json +18 -0
- sentinelworks-0.1.0/tests/vectors/decide/shadow_from_policy.json +21 -0
- sentinelworks-0.1.0/tests/vectors/decide/shadow_with_overlay.json +43 -0
- sentinelworks-0.1.0/uv.lock +5500 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# generated (LLD 2, 4)
|
|
2
|
+
contracts/gen/
|
|
3
|
+
contracts/.build/
|
|
4
|
+
go/pkg/contracts/
|
|
5
|
+
sdk/python/src/sentinelworks/_gen/
|
|
6
|
+
jobs/_gen/
|
|
7
|
+
sdk/python/src/sentinelworks/assets/
|
|
8
|
+
sdk/python/src/sentinelworks/adapters/*/declarations/*.json
|
|
9
|
+
console/src/api/gen/
|
|
10
|
+
# build output
|
|
11
|
+
/bin/
|
|
12
|
+
/dist/
|
|
13
|
+
console/dist/
|
|
14
|
+
# tool caches
|
|
15
|
+
.venv/
|
|
16
|
+
node_modules/
|
|
17
|
+
__pycache__/
|
|
18
|
+
.mypy_cache/
|
|
19
|
+
.ruff_cache/
|
|
20
|
+
.pytest_cache/
|
|
21
|
+
.benchmarks/
|
|
22
|
+
# Terraform (commit .terraform.lock.hcl)
|
|
23
|
+
.terraform/
|
|
24
|
+
*.tfstate
|
|
25
|
+
*.tfstate.*
|
|
26
|
+
*.tfplan
|
|
27
|
+
# secrets and local settings
|
|
28
|
+
.env
|
|
29
|
+
.env.*
|
|
30
|
+
.claude/settings.local.json*
|
|
31
|
+
# Terraform crash logs (WP-02 3)
|
|
32
|
+
infra/**/crash.log
|
|
33
|
+
# Offline policy render of infra/bootstrap (WP-02 12.5): generated by make -C infra tf-test
|
|
34
|
+
infra/bootstrap/tests/render/out/
|
|
35
|
+
|
|
36
|
+
# Image digests written by the pipeline (WP-06 3, WP-21)
|
|
37
|
+
infra/envs/**/images.auto.tfvars.json
|
|
38
|
+
# Shared modules carry no lock file (WP-06 7.2); terraform test creates one locally
|
|
39
|
+
infra/modules/*/.terraform.lock.hcl
|
|
40
|
+
# contracts test report (WP-03 11)
|
|
41
|
+
contracts/tests/report/
|
|
42
|
+
workers/indexer/spike/results/
|
|
43
|
+
images.auto.tfvars.json
|
|
44
|
+
control/control-api/internal/compliance/controls.embedded.yaml
|
|
45
|
+
|
|
46
|
+
# Generated by tools/policy/export_job_assets.py (WP-45 7.3)
|
|
47
|
+
jobs/rule_learner/assets/*
|
|
48
|
+
!jobs/rule_learner/assets/.gitkeep
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: sentinelworks
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
License-File: NOTICE
|
|
5
|
+
Requires-Python: <3.14,>=3.10
|
|
6
|
+
Requires-Dist: cryptography
|
|
7
|
+
Requires-Dist: google-re2
|
|
8
|
+
Requires-Dist: httpx
|
|
9
|
+
Requires-Dist: msgspec
|
|
10
|
+
Requires-Dist: orjson
|
|
11
|
+
Requires-Dist: wasmtime==50.0.1
|
|
12
|
+
Requires-Dist: zstandard
|
|
13
|
+
Provides-Extra: aws
|
|
14
|
+
Requires-Dist: botocore; extra == 'aws'
|
|
15
|
+
Provides-Extra: crewai
|
|
16
|
+
Requires-Dist: crewai<1.16,>=1.15.29; extra == 'crewai'
|
|
17
|
+
Provides-Extra: langchain
|
|
18
|
+
Requires-Dist: langchain<1.5,>=1.3.9; extra == 'langchain'
|
|
19
|
+
Requires-Dist: langgraph-sdk>=0.4.4; extra == 'langchain'
|
|
20
|
+
Requires-Dist: langgraph<1.3,>=1.2; extra == 'langchain'
|
|
21
|
+
Provides-Extra: spiffe
|
|
22
|
+
Requires-Dist: spiffe; extra == 'spiffe'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# ASK and resume
|
|
2
|
+
|
|
3
|
+
An ASK verdict pauses the run. The adapter raises a framework interrupt with this payload:
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{"request_id": "...", "tool": "...", "approver_class": "OPERATOR", "expires_at": "...", "kid": "...", "mac": "..."}
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
To resume, send the payload back with the decision, under the key `sentinel`:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{"sentinel": {"request_id": "...", "tool": "...", "approver_class": "OPERATOR", "expires_at": "...", "kid": "...", "mac": "...", "decision": "APPROVE", "approver_token": "..."}}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The process that resumes need not be the one that paused: it verifies `kid` and `mac` itself. The request id depends on the thread, the call id and the arguments, not on the instance, so replicas agree.
|
|
16
|
+
|
|
17
|
+
Rules:
|
|
18
|
+
|
|
19
|
+
- A resume is single use. A second resume of the same value is ignored.
|
|
20
|
+
- `decision` is `APPROVE` or `REJECT`. A reject denies the call and needs no approver token.
|
|
21
|
+
- A resume after `expires_at` is denied.
|
|
22
|
+
- The policy runs again on resume, and the stricter verdict wins.
|
|
23
|
+
- An approver token must satisfy the rule's approver class. A token of a lower class leaves the run waiting.
|
|
24
|
+
|
|
25
|
+
Residual risk: an approver token is bound to one request id, so a replayed token fails for any other request. A checkpoint restored to before a resume can present the same request again; the capsule's `asks_done` list is what stops it, so a rewound capsule is the case to watch.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Supported framework versions
|
|
2
|
+
|
|
3
|
+
| Framework | Versions | Conformance | ASK |
|
|
4
|
+
|---|---|---|---|
|
|
5
|
+
| LangChain | `>=1.3.9,<1.5` with LangGraph `>=1.2,<1.3` | TESTED | yes, with a durable checkpointer |
|
|
6
|
+
| CrewAI | `>=1.15.29,<1.16` | TESTED | no: an ASK verdict is a DENY |
|
|
7
|
+
|
|
8
|
+
**LangChain 1.5 (pending V-04).** On 2026-11-13 the LangChain conformance suite, the V-04 ASK tests, the adapter unit tests and the canary passed on `langchain` 1.5.0, `langchain-core` 1.8.0 and `langgraph` 1.2.21 with the range widened to `<1.6`. The range stays `<1.5` until the owner records V-04 for 1.5.
|
|
9
|
+
|
|
10
|
+
A weekly job runs the conformance suite against the newest releases and opens an issue labelled `sdk-compat` on a failure. It never blocks a release. A person then decides V-04 (LangChain) or V-06 (CrewAI) for the new version.
|
|
11
|
+
|
|
12
|
+
If ASK fails on one framework version, remove `ASK` from that version's declaration. The runtime then answers DENY with `ADAPTER_CAPABILITY` for ASK verdicts on that version, which is the stricter outcome.
|
|
13
|
+
|
|
14
|
+
## CrewAI
|
|
15
|
+
|
|
16
|
+
Facts below were read from CrewAI 1.15 and are checked by the V-06 suite (`tests/conformance/v06`).
|
|
17
|
+
|
|
18
|
+
| Item | Outcome |
|
|
19
|
+
|---|---|
|
|
20
|
+
| Lowest version | 1.15.29, the first that passes the whole suite. Hook behaviour changed inside 1.15: on 1.15.4 a `HookAborted` before a model call arrives as a `ValueError`, so `PolicyError` would be lost |
|
|
21
|
+
| Tool block | `HOOK_BLOCK_THEN_RESULT`: the hook stops the call, and the after-tool hook's return value is what the agent reads |
|
|
22
|
+
| Capsule | `FLOW_STATE` in a persisted flow whose state derives from `SentinelFlowState`; `RUN_SCOPED` otherwise |
|
|
23
|
+
| ASK | absent. A tool hook cannot pause: CrewAI swallows the exception and runs the tool (V06-09) |
|
|
24
|
+
| Streaming windows | absent |
|
|
25
|
+
|
|
26
|
+
**Failures.** CrewAI swallows an ordinary exception from a hook and goes ahead with the call. The adapter catches everything in its hooks and blocks instead. An application's own hook is not covered by this.
|
|
27
|
+
|
|
28
|
+
**Process-wide hooks.** Hooks cover every crew in the process, instrumented or not, so all of them share one `agent_id`. The manifest lists each crew seen. A crew that was not instrumented has no kickoff callbacks, so it gets Pre-LLM to Post-Tool decisions but no Prompt Submit or Agent Response.
|
|
29
|
+
|
|
30
|
+
**After a deny.** A denied run makes no model call: each agent answers from a local stand-in, and the real model is put back when the run ends. An agent the swap cannot reach, such as a hierarchical crew's manager or a `kickoff_for_each` copy, stops the run with `PolicyError` instead.
|
|
31
|
+
|
|
32
|
+
**`kickoff_for_each`.** Each item is its own run on its own thread id, so per-thread ledger counters do not add up across items.
|
|
33
|
+
|
|
34
|
+
**A kickoff that raises.** CrewAI skips the after-kickoff callbacks, so if something outside Sentinel raises during a run, that run has no Task End or Agent Response record, and a task description that was masked stays masked.
|
|
35
|
+
|
|
36
|
+
**chromadb.** `sentinelworks[crewai]` installs `chromadb` 1.5.9, because CrewAI requires it and does not import without it. That version has known advisories with no fix yet, including a pre-auth code injection (CVE-2026-45829, CVE-2026-45830, CVE-2026-45831, CVE-2026-45833). They read as flaws in the ChromaDB server, and CrewAI embeds it as a library, so do not run a ChromaDB server from this environment or expose one to a network. The base `sentinelworks` install does not include it.
|
|
37
|
+
|
|
38
|
+
**Telemetry.** CrewAI sends its own telemetry by default. Set `CREWAI_DISABLE_TELEMETRY=true`, `OTEL_SDK_DISABLED=true` and `CREWAI_TRACING_ENABLED=false` to turn it off. Sentinel does not change this for you.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Workload credentials
|
|
2
|
+
|
|
3
|
+
`SENTINEL_CREDENTIAL=auto` takes the first provider that is available, in this order: enrollment token, Kubernetes token file, SPIFFE, AWS, GCP, Azure, OIDC file. Set a name to use one provider only. A named provider that is not available keeps retrying with backoff.
|
|
4
|
+
|
|
5
|
+
The SDK makes one attempt per call. Tokens, paths and URLs never appear in logs or errors.
|
|
6
|
+
|
|
7
|
+
## Kubernetes
|
|
8
|
+
|
|
9
|
+
Project a service-account token to `SENTINEL_K8S_TOKEN_PATH` (default `/var/run/secrets/sentinel/token`). The file is read on every renewal, so a rotated token is picked up.
|
|
10
|
+
|
|
11
|
+
## AWS
|
|
12
|
+
|
|
13
|
+
Install `sentinelworks[aws]`. The workload needs an IAM role: ECS or EKS pod identity, IRSA, EC2 instance metadata, or session credentials. Long-lived user keys are not offered, because the cell accepts roles only.
|
|
14
|
+
|
|
15
|
+
The SDK signs an STS `GetCallerIdentity` request for `us-east-1`, wherever the workload runs. The host clock must be within 5 minutes of true time.
|
|
16
|
+
|
|
17
|
+
## GCP
|
|
18
|
+
|
|
19
|
+
No extra package. The SDK asks the metadata server for an identity token with `format=full`, and the audience is the cell URL.
|
|
20
|
+
|
|
21
|
+
## Azure
|
|
22
|
+
|
|
23
|
+
No extra package. The SDK uses IMDS, or the App Service endpoint when `IDENTITY_ENDPOINT` and `IDENTITY_HEADER` are set. Set `AZURE_CLIENT_ID` for a user-assigned identity.
|
|
24
|
+
|
|
25
|
+
Register the cell URL as an application identifier URI in your Entra directory. The `resource` of the token request must equal it.
|
|
26
|
+
|
|
27
|
+
## SPIFFE
|
|
28
|
+
|
|
29
|
+
Install `sentinelworks[spiffe]` to use the Workload API through `SPIFFE_ENDPOINT_SOCKET`. Or have a helper write a JWT-SVID to `SENTINEL_SPIFFE_JWT_PATH`, which needs no package; the helper then sets the audience.
|
|
30
|
+
|
|
31
|
+
## OIDC
|
|
32
|
+
|
|
33
|
+
Point `SENTINEL_OIDC_TOKEN_PATH` at a token file. The file is read on every renewal. The audience is the one your platform put in the token, and the cell checks it.
|
|
34
|
+
|
|
35
|
+
## Hosts without workload identity
|
|
36
|
+
|
|
37
|
+
A one-time enrollment token is single use. The instance that uses it then renews by key possession, but only while the process lives: a restarted process needs a new token, and a reused one gets `403 enrollment_token_unknown`. Platforms that restart or sleep instances, and give them no workload identity (free Render or Hugging Face instances, for example), therefore need a fresh token for every start. Your deployment would have to mint one through the control API before the process starts, and these platforms have no step for that.
|
|
38
|
+
|
|
39
|
+
Until there is a supported method for such hosts, run the agent where it has a workload identity: Kubernetes, AWS, GCP, Azure, SPIFFE or an OIDC token file. `SENTINEL_OUTBOX_DIR` on a temporary filesystem loses records that were not uploaded before a restart. The cell then has a gap in the evidence for that instance.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# LangChain adapter
|
|
2
|
+
|
|
3
|
+
## 0. Get the SDK
|
|
4
|
+
|
|
5
|
+
The distribution and the import package are both `sentinelworks`. Install only that name. `sentinel-sdk` on public PyPI is an unrelated project.
|
|
6
|
+
|
|
7
|
+
- From PyPI, once the first release is published: `pip install "sentinelworks[langchain]"`.
|
|
8
|
+
- Before that, use the wheel from an `sdk-release` run, which your Sentinelworks contact sends you: `pip install "sentinelworks[langchain] @ file:///path/to/sentinelworks-<version>-py3-none-any.whl"`.
|
|
9
|
+
|
|
10
|
+
A `git+https://` install of the repository does not work, because the wheel carries generated code and assets that are not in git.
|
|
11
|
+
|
|
12
|
+
## 1. Configure and start
|
|
13
|
+
|
|
14
|
+
Set these in the deployment's environment, not in code:
|
|
15
|
+
|
|
16
|
+
| Variable | Value |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `SENTINEL_AGENT_ID` | The agent_id you registered in the console, such as `demo/support-agent` |
|
|
19
|
+
| `SENTINEL_CELL_URL` | The edge URL of your cell (`https://`) |
|
|
20
|
+
| `SENTINEL_ENROLLMENT_TOKEN` | Only for the one-time token method: the token from the console |
|
|
21
|
+
| `SENTINEL_OUTBOX_DIR` | A writable directory for records waiting to upload |
|
|
22
|
+
|
|
23
|
+
`sentinelworks.init(cell_url=..., agent_id=...)` takes the same two values as arguments, and an argument wins over the environment variable. `init()` raises `ConfigError` when either value is missing or malformed. The cell refuses to enroll an instance whose agent_id is not registered or does not match its binding or enrollment token, so use exactly the agent_id shown in the console.
|
|
24
|
+
|
|
25
|
+
A complete, runnable setup:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import sentinelworks
|
|
29
|
+
from langchain.agents import create_agent
|
|
30
|
+
from langgraph.checkpoint.postgres import PostgresSaver
|
|
31
|
+
from sentinelworks.langchain import SentinelMiddleware
|
|
32
|
+
|
|
33
|
+
sentinelworks.init() # once per process, before building the agent
|
|
34
|
+
|
|
35
|
+
with PostgresSaver.from_conn_string(DB_URL) as saver:
|
|
36
|
+
saver.setup()
|
|
37
|
+
agent = create_agent(
|
|
38
|
+
model,
|
|
39
|
+
tools,
|
|
40
|
+
middleware=[SentinelMiddleware()], # last in the list
|
|
41
|
+
checkpointer=saver,
|
|
42
|
+
)
|
|
43
|
+
result = agent.invoke(
|
|
44
|
+
{"messages": [{"role": "user", "content": "hi"}]}, {"configurable": {"thread_id": "t-1"}}
|
|
45
|
+
)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Put `SentinelMiddleware()` last in the list. It is then innermost on model and tool calls, so it sees the arguments after every other middleware has rewritten them.
|
|
49
|
+
|
|
50
|
+
### Constructor arguments
|
|
51
|
+
|
|
52
|
+
`SentinelMiddleware()` takes keyword arguments only. It has no `agent_id` argument: the agent_id comes from `init()`.
|
|
53
|
+
|
|
54
|
+
| Argument | Default | Meaning |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `checkpointer` | detected | The agent's checkpointer, when the middleware cannot find it on its own |
|
|
57
|
+
| `retriever_tools` | `()` | Names of tools whose output is retrieved content (RAG); `create_retriever_tool` tools are detected |
|
|
58
|
+
| `delegation_tools` | `()` | Names of tools that hand work to another agent |
|
|
59
|
+
| `agent_name` | `None` | A label for this agent within the process, reported in the manifest |
|
|
60
|
+
|
|
61
|
+
It raises `NotInitialisedError` when `init()` has not run.
|
|
62
|
+
|
|
63
|
+
## Supported versions
|
|
64
|
+
|
|
65
|
+
The tested range is LangChain `>=1.3.9,<1.5` with LangGraph `>=1.2,<1.3`, and the `[langchain]` extra caps it, so `pip install "sentinelworks[langchain]"` resolves a tested version. An application that pins `langchain==1.5.0` gets a resolver conflict. If you force a newer version anyway, it loads as `UNTESTED`: the SDK logs the warning `adapter.untested_version`, ASK is off (an ASK verdict becomes a denial), and the manifest shows it. Support for LangChain 1.5 is pending a V-04 decision; see [compat](compat.md).
|
|
66
|
+
|
|
67
|
+
## What your application sees
|
|
68
|
+
|
|
69
|
+
| Outcome | Result |
|
|
70
|
+
|---|---|
|
|
71
|
+
| Allowed | The normal result |
|
|
72
|
+
| Denied at Session Start or Prompt Submit | `invoke` returns normally, and the last message is the refusal text |
|
|
73
|
+
| Denied at Pre-LLM or Post-LLM | `sentinelworks.PolicyError` is raised from `invoke` or `stream` |
|
|
74
|
+
| Denied at Pre-Tool | The model receives a structured refusal as the tool result, and the run goes on unless the policy ended it |
|
|
75
|
+
| Denied at Agent Response | The last message is the safe-response text |
|
|
76
|
+
|
|
77
|
+
## Tools
|
|
78
|
+
|
|
79
|
+
Use these helpers when the manifest needs more than LangChain knows:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from sentinelworks.langchain import declare_tool, tag_mcp_tools
|
|
83
|
+
|
|
84
|
+
declare_tool(refund, action_class="TRANSFER_VALUE", value_arg="amount", subject_arg="customer_id")
|
|
85
|
+
tools = tag_mcp_tools(await client.get_tools(), "crm")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`declare_tool` is a hint for the manifest. The admin catalog still decides how a tool is treated. An untagged MCP tool gets the server ID `unknown`.
|
|
89
|
+
|
|
90
|
+
Retriever tools from `create_retriever_tool` are detected. For other retrievers or for delegation tools, pass the names: `SentinelMiddleware(retriever_tools=["kb"], delegation_tools=["helper"])`.
|
|
91
|
+
|
|
92
|
+
## Capsule
|
|
93
|
+
|
|
94
|
+
The middleware adds a `sentinel_capsule` field to the agent state, and your checkpointer stores it with the thread. This needs a `thread_id` and a checkpointer the middleware can see. If it cannot detect yours, pass it: `SentinelMiddleware(checkpointer=saver)`. Without one, the agent runs run-scoped, with no capsule and no ASK.
|
|
95
|
+
|
|
96
|
+
## ASK
|
|
97
|
+
|
|
98
|
+
ASK works on a tested LangChain version with a durable checkpointer: `PostgresSaver`, `AsyncPostgresSaver`, `SqliteSaver` or `AsyncSqliteSaver`. With `InMemorySaver`, with no checkpointer, or on an `UNTESTED` version, an ASK verdict becomes a denial (fallback `ADAPTER_CAPABILITY`). The agent's human channel must not be `NONE`. An ASK appears as a LangGraph interrupt:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from langgraph.types import Command
|
|
102
|
+
|
|
103
|
+
result = agent.invoke(inputs, config)
|
|
104
|
+
payload = result["__interrupt__"][0].value["sentinel"]
|
|
105
|
+
agent.invoke(
|
|
106
|
+
Command(resume={"sentinel": {**payload, "decision": "APPROVE", "approver_token": token}}),
|
|
107
|
+
config,
|
|
108
|
+
)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Send the payload back unchanged. `decision` is `APPROVE` or `REJECT`. An operator rule needs a verified `approver_token`.
|
|
112
|
+
|
|
113
|
+
## Other middleware
|
|
114
|
+
|
|
115
|
+
- **Order.** Keep `SentinelMiddleware()` last, after `PIIMiddleware`, call limits, `@dynamic_prompt` and `HumanInTheLoopMiddleware`. Sentinel then decides on what the model and tools actually receive.
|
|
116
|
+
- **Human-in-the-loop.** LangChain's `HumanInTheLoopMiddleware` and Sentinel's ASK are separate interrupts. Sentinel's carries a `sentinel` key in its value, so tell them apart by that key. Answer each interrupt with its own resume. Running both in the same turn is not covered by the conformance suite yet. Test it in your app before relying on it.
|
|
117
|
+
- **Streaming.** `agent.stream(...)` and `astream` are covered, with the limits below.
|
|
118
|
+
|
|
119
|
+
## Limitations
|
|
120
|
+
|
|
121
|
+
- **Streaming.** Agent Response runs on each sentence window before its text is released. Text already released stays with the application when a later window is denied.
|
|
122
|
+
- **Split secrets.** Each window is scanned on its own, so a secret split across two windows is not found here. The cell's leak scanner is the backstop.
|
|
123
|
+
- **Structured output.** It is not streamed through the window check. The complete message is checked at the end of the run.
|
|
124
|
+
- **Python 3.10 with async.** asyncio tasks carry no context on 3.10, so hooks cannot read the `thread_id`. Async runs there are run-scoped: decisions still apply, but there is no capsule and no ASK. LangGraph also streams no async tokens there, so the complete answer is checked at the end of the run. Use Python 3.11 or later for async agents.
|
|
125
|
+
- **Middleware order.** The manifest lists Sentinel's hooks, not its position among other middleware, because a middleware cannot see the others.
|
|
126
|
+
- **Session End.** LangChain has no signal for a closed thread, so Session End fires when a thread has been idle for 30 minutes.
|
|
127
|
+
- **Hosts without workload identity** (free Render or Hugging Face instances, for example). See [credentials](credentials.md#hosts-without-workload-identity).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Shutdown
|
|
2
|
+
|
|
3
|
+
`sentinelworks.init()` installs a `SIGTERM` handler and registers an `atexit` hook. Both drain the outbox, so decision records written before an orderly exit reach the cell.
|
|
4
|
+
|
|
5
|
+
## What happens on SIGTERM
|
|
6
|
+
|
|
7
|
+
1. The runtime enters `DRAINING` and refuses new runs.
|
|
8
|
+
2. The SDK uploads what is queued. It uses up to 20 seconds in total.
|
|
9
|
+
3. It appends a `SHUTDOWN` record with the number of records acknowledged during the drain and the number left, then uploads that too.
|
|
10
|
+
4. It releases the outbox lock. An instance directory with nothing left in it is deleted.
|
|
11
|
+
5. It calls the handler your application had installed before `init()`. If there was none, it re-raises the default action, so the process ends with the usual `SIGTERM` status.
|
|
12
|
+
|
|
13
|
+
Set your platform's termination grace period above 20 seconds. In Kubernetes, keep `terminationGracePeriodSeconds` at 30 or more.
|
|
14
|
+
|
|
15
|
+
## Chaining with your own handler
|
|
16
|
+
|
|
17
|
+
Install your handler before you call `sentinelworks.init()`. The SDK remembers it and calls it after the drain. A handler installed after `init()` replaces the SDK's handler, so call `sentinelworks.shutdown()` from it yourself.
|
|
18
|
+
|
|
19
|
+
## Without a main thread
|
|
20
|
+
|
|
21
|
+
Python allows signal handlers only on the main thread. If `init()` runs on another thread, the SDK installs no handler and relies on `atexit`. `atexit` does not run on an unhandled `SIGTERM`, so in that case handle the signal yourself and call `sentinelworks.shutdown()`.
|
|
22
|
+
|
|
23
|
+
## What can be lost
|
|
24
|
+
|
|
25
|
+
| Event | File mode (`SENTINEL_OUTBOX_DIR` set) | Memory mode |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| Orderly exit, drain finished | Nothing | Nothing |
|
|
28
|
+
| Grace period runs out | Nothing: the next process recovers the directory | Records not yet acknowledged are lost; the indexer records a gap |
|
|
29
|
+
| `SIGKILL` or a process crash | Nothing the operating system has received; a torn last line is skipped | The queue is lost; the indexer records a gap |
|
|
30
|
+
| Power loss or an OS crash | Records still in the page cache, because the outbox does not `fsync` | The queue is lost; the indexer records a gap |
|
|
31
|
+
|
|
32
|
+
The next process recovers a directory only if its `agent_id` matches and no live process holds the lock. It uploads those records with `recovery.instance_id` and then deletes the directory.
|
|
33
|
+
|
|
34
|
+
An instance that has been revoked, or that never got a token, does not upload during a drain. In file mode its records stay on disk.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sentinelworks"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
license-files = ["NOTICE"]
|
|
9
|
+
requires-python = ">=3.10,<3.14" # LLD 17
|
|
10
|
+
dependencies = ["msgspec", "httpx", "zstandard", "cryptography", "google-re2", "orjson", "wasmtime==50.0.1"] # LLD 17; uv.lock pins; wasmtime is the V-02 version (docs/adr/V-02/decision.json)
|
|
11
|
+
[project.optional-dependencies]
|
|
12
|
+
langchain = ["langchain>=1.3.9,<1.5", "langgraph>=1.2,<1.3", "langgraph-sdk>=0.4.4"] # conformance-tested range. LLD 18 says <1.2; every release below 1.3.9 and langgraph-sdk below 0.4.4 carries a known advisory (PYSEC-2026-2192, PYSEC-2026-4240), and scan.sh gives the SDK no exceptions
|
|
13
|
+
crewai = ["crewai>=1.15.29,<1.16"] # WP-26, V-06: the first release that passes the whole V-06 and conformance suite
|
|
14
|
+
aws = ["botocore"]
|
|
15
|
+
spiffe = ["spiffe"]
|
|
16
|
+
|
|
17
|
+
[dependency-groups]
|
|
18
|
+
dev = ["botocore", "crewai>=1.15.29,<1.16", "pytest<9.2", "pytest-benchmark", "mypy", "hypothesis", "jsonschema", "pyyaml", "langchain>=1.3.9,<1.5", "langgraph>=1.2,<1.3", "langgraph-sdk>=0.4.4", "langgraph-checkpoint-sqlite", "langgraph-checkpoint-postgres", "psycopg[binary]>=3"] # pytest-benchmark 5.3.0 on pytest 9.2.0: AttributeError: 'Function' object has no attribute '_nodeid'. Remove when pytest-benchmark supports pytest 9.2
|
|
19
|
+
|
|
20
|
+
[tool.hatch.version]
|
|
21
|
+
path = "src/sentinelworks/__about__.py" # semver: the manifest and event contracts require it
|
|
22
|
+
|
|
23
|
+
[tool.hatch.build.targets.wheel]
|
|
24
|
+
packages = ["src/sentinelworks"]
|
|
25
|
+
artifacts = ["src/sentinelworks/_gen", "src/sentinelworks/assets", "src/sentinelworks/adapters/*/declarations/*.json"] # generated and gitignored, but must be in the wheel
|
|
26
|
+
|
|
27
|
+
[tool.pytest.ini_options]
|
|
28
|
+
testpaths = ["tests"]
|
|
29
|
+
python_files = ["test_*.py", "bench_*.py"]
|
|
30
|
+
python_functions = ["test_*", "bench_*"]
|
|
31
|
+
pythonpath = ["."]
|
|
32
|
+
addopts = "--strict-markers"
|
|
33
|
+
markers = ["integration", "awsdev", "bench", "deployed", "canary", "conformance", "slow"]
|
|
34
|
+
|
|
35
|
+
[tool.mypy]
|
|
36
|
+
strict = true
|
|
37
|
+
mypy_path = "src"
|
|
38
|
+
|
|
39
|
+
[[tool.mypy.overrides]]
|
|
40
|
+
module = ["sentinelworks._gen.*", "_gen.*"]
|
|
41
|
+
ignore_errors = true
|
|
42
|
+
|
|
43
|
+
[[tool.mypy.overrides]]
|
|
44
|
+
module = ["re2", "yaml", "jsonschema", "botocore.*", "spiffe.*", "spiffe"]
|
|
45
|
+
ignore_missing_imports = true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Sentinel Framework SDK and Sentinel Runtime (LLD 17)."""
|
|
2
|
+
|
|
3
|
+
from sentinelworks.__about__ import __version__
|
|
4
|
+
from sentinelworks.api import init, set_principal, shutdown, status
|
|
5
|
+
from sentinelworks.errors import PolicyError
|
|
6
|
+
|
|
7
|
+
__all__ = ["PolicyError", "__version__", "init", "set_principal", "shutdown", "status"]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""base64url without padding, strict on decode, and the lowercase base32 helper."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import base64
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
_B64URL = re.compile(r"^[A-Za-z0-9_-]*$")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def b64u_encode(data: bytes) -> str:
|
|
12
|
+
return base64.urlsafe_b64encode(data).rstrip(b"=").decode("ascii")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def b64u_decode(text: str) -> bytes:
|
|
16
|
+
"""Decode strictly: no padding, the URL alphabet only, canonical trailing bits."""
|
|
17
|
+
if not _B64URL.fullmatch(text) or len(text) % 4 == 1:
|
|
18
|
+
raise ValueError("bad base64url")
|
|
19
|
+
data = base64.urlsafe_b64decode(text + "=" * (-len(text) % 4))
|
|
20
|
+
if b64u_encode(data) != text:
|
|
21
|
+
raise ValueError("bad base64url")
|
|
22
|
+
return data
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def b32_lower_nopad(data: bytes) -> str:
|
|
26
|
+
return base64.b32encode(data).rstrip(b"=").decode("ascii").lower()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Access token claims, decoded without trust, for binding checks only (WP-10 4.5.5).
|
|
2
|
+
|
|
3
|
+
Outside transport/ so the no-I/O runtime can read them (lint rule SIO005).
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
from sentinelworks._b64 import b64u_decode
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True, slots=True)
|
|
15
|
+
class AccessClaims:
|
|
16
|
+
tenant: str
|
|
17
|
+
agent_id: str
|
|
18
|
+
env: str
|
|
19
|
+
kind: str
|
|
20
|
+
sub: str
|
|
21
|
+
exp: int
|
|
22
|
+
pending: bool
|
|
23
|
+
cell: str | None
|
|
24
|
+
cnf_jkt: str | None = None # the key the token is bound to (WP-12 5.11)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def decode_access_claims(access_token: str) -> AccessClaims:
|
|
28
|
+
"""Base64url decode of the middle part. Raises ValueError."""
|
|
29
|
+
parts = access_token.split(".")
|
|
30
|
+
if len(parts) != 3:
|
|
31
|
+
raise ValueError("not a compact token")
|
|
32
|
+
try:
|
|
33
|
+
claims = json.loads(b64u_decode(parts[1]))
|
|
34
|
+
return AccessClaims(
|
|
35
|
+
tenant=claims["tenant"],
|
|
36
|
+
agent_id=claims["agent_id"],
|
|
37
|
+
env=claims["env"],
|
|
38
|
+
kind=claims.get("kind", "APPLICATION"),
|
|
39
|
+
sub=claims["sub"],
|
|
40
|
+
exp=int(claims["exp"]),
|
|
41
|
+
pending=bool(claims.get("pending", False)),
|
|
42
|
+
cell=claims.get("cell"),
|
|
43
|
+
cnf_jkt=(claims.get("cnf") or {}).get("jkt"),
|
|
44
|
+
)
|
|
45
|
+
except (AttributeError, KeyError, TypeError) as exc:
|
|
46
|
+
raise ValueError("bad claims") from exc
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Cell time and monotonic-adjusted host time (WP-10 5.21)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
|
|
9
|
+
_BASE_WALL = time.time()
|
|
10
|
+
_BASE_MONO = time.monotonic()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def format_rfc3339_ms(epoch_s: float) -> str:
|
|
14
|
+
moment = datetime.fromtimestamp(round(epoch_s, 3), tz=timezone.utc)
|
|
15
|
+
return moment.strftime("%Y-%m-%dT%H:%M:%S.") + f"{moment.microsecond // 1000:03d}Z"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def parse_rfc3339(text: str) -> float:
|
|
19
|
+
"""Epoch seconds of an RFC 3339 UTC timestamp. Raises ValueError."""
|
|
20
|
+
return datetime.fromisoformat(text.replace("Z", "+00:00")).timestamp()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def host_now_iso() -> str:
|
|
24
|
+
return format_rfc3339_ms(_BASE_WALL + (time.monotonic() - _BASE_MONO))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CellClock:
|
|
28
|
+
"""Expiry checks use this, never the host wall clock."""
|
|
29
|
+
|
|
30
|
+
def __init__(self) -> None:
|
|
31
|
+
self._lock = threading.Lock()
|
|
32
|
+
self._server_s: float | None = None
|
|
33
|
+
self._mono_at_sync = 0.0
|
|
34
|
+
|
|
35
|
+
def now(self) -> float:
|
|
36
|
+
server = self._server_s
|
|
37
|
+
if server is None:
|
|
38
|
+
return time.time()
|
|
39
|
+
return server + (time.monotonic() - self._mono_at_sync)
|
|
40
|
+
|
|
41
|
+
def now_s(self) -> float:
|
|
42
|
+
return self.now()
|
|
43
|
+
|
|
44
|
+
def update(self, server_time_s: float) -> None:
|
|
45
|
+
"""WP-12 calls this with the verified sync state `iat`. Lower values are ignored."""
|
|
46
|
+
with self._lock:
|
|
47
|
+
if self._server_s is None or server_time_s >= self._server_s:
|
|
48
|
+
self._server_s = server_time_s
|
|
49
|
+
self._mono_at_sync = time.monotonic()
|
|
File without changes
|