Safety Harness for Physical AI
A perception-grounded safety harness for physical AI: a default-deny precondition gate for
physical actuators. Every action a robot proposes — grasp, place, reach, anything else you
register — must earn a PERMIT from measured, structured evidence before it executes. No
evidence, or a failed check, or an adapter that raises: BLOCK. Permission, not detection.
Full design spec, architecture, worked examples, test/validation record, and roadmap: Perception-Grounded Safety Harness for Physical AI — design doc.
Status
Reference implementation, not yet independently assessed. Of its 31 precondition checks, 24 are confirmed blocking live in one simulator (Isaac Lab) on two robots, a Franka Panda arm and an ANYmal-C quadruped:
- the hazards were physical in the simulator, commanded by the action, or injected into the otherwise-real world state;
- 1 more check was evaluated live but never the reason for a block; 2 more (
joint_velocity_within_limits,joint_effort_within_limits) are wired into the example config as of 0.3.4 but not yet re-validated live; 4 are registered but not wired into the example config; - see the design doc's "Live Re-validation of the Wired Set" for the per-check record.
Plus 239 automated unit/fuzz/mutation/black-box/stress tests. A third adapter (a
Unitree G1 humanoid with a dexterous hand) ships with a trained block-stacking policy to gate — see
examples/isaac_lab_g1_stack; gating that policy under injected
hazards is in progress, not yet a validation result. It has not
been reviewed by a functional-safety assessor
against IEC 61508, ISO 13849, or ISO 10218/TS 15066 — see the design doc's Scope & Non-Goals
section for what's out of scope today (joint-space kinematic checks, certified numeric
thresholds, data-protection handling of logged human-position data). Treat this as engineering
evidence, not a certification.
What's here
safety_harness/schema.py— the data contract (WorldState,Action,Decision, and friends).SCHEMA_VERSIONtracks this contract specifically; bump it deliberately (see the design doc's Version History).safety_harness/engine.py—ActuatorGate.gate(), the single decision entry point.safety_harness/preconditions.py— the registered precondition checks (31 distinct checks, one of which,surface_confirmed_stable, is deprecated in favor ofdestination_confirmed_stable_and_clear; the registry also keepsbalance_margin_maintainedas a legacy alias ofstability_margin_maintained): object/target safety, placement, agent proximity incl. ISO/TS 15066, vulnerable bystanders, robot self-limits, payload/grip force, stability, perception integrity (sensor freshness, swept-path coverage), decision deadline, command and configuration integrity, environmental signals.safety_harness/watchdog.py—DecisionWatchdog, an actuator-side dead-man's switch: execute only a PERMIT that is still fresh and bit-identical to what was checked, otherwise freeze.safety_harness/integrity.py/safety_harness/pin.py— action digests and configuration pinning (python -m safety_harness.pin configs/example_action_schema.yaml > configs/example_action_schema.yaml.sha256regenerates the pin; the command prints the digest).safety_harness/audit_log.py— persistent, hash-chained (optionally HMAC-keyed) logging:HashChainedDecisionLogger(a drop-inLogger) andSoftwareVersionLogfor durable, tamper-evident records;report_identity()reports the running harness's own schema/package version and pinned config digest at any time.verify_log()re-walks a log file end to end.safety_harness/action_schema.py— the YAML-driven registry mapping action types to the checks they must pass (configs/example_action_schema.yamlis the reference wiring).safety_harness/sbom.py— CycloneDX Software Bill of Materials generation (python -m safety_harness.sbom > examples/sbom/results/safety-harness.cyclonedx.jsonregenerates it; seeexamples/sbom/for the checked-in reference output and why CycloneDX, not SPDX). Reports exactly one runtime third-party dependency (pyyaml) — see that module's docstring for why that's the honest answer, not "stdlib only, zero dependencies."safety_harness/segmentation.py—GripActionSegmenter, turning a continuous grip-based policy's control stream into the discretegrasp/place/reachproposalsgate()expects, by watching for edges in the commanded grip rather than gating every step — fixes the deadlock a naive per-step "grasp" proposal causes once the fingers make contact (found live on a G1 humanoid stacking task; the fix this extracts is what the closed-loop Franka measurement already validates).tests/— unit tests, mutation/random-fuzz tests, and reflection-driven black-box contract tests.
Robot-specific behavior lives entirely behind four adapter interfaces
(PerceptionAdapter, DynamicsAdapter, FallbackController, Logger) so the engine and checks
are robot-agnostic. Reference adapters exist for three Isaac Lab robots: Franka Panda
(adapters/isaac_lab.py), ANYmal-C (adapters/isaac_lab_anymal.py) and Unitree G1
(adapters/isaac_lab_g1.py).
Not yet independently verified (deliberately flagged, not buried): the ISO/TS 15066 Table A.2
body-region force figures, the child clearance and the crowd factors in
vulnerable_bystander_protected are placeholder values that need sign-off from a qualified safety
engineer before being described as standards-aligned; and config_integrity_verified is a
checksum unless you supply an HMAC key — it catches corruption and uncoordinated edits, not an
attacker who can rewrite both the config and its pinned hash.
Install & test
pip install safety-harness
Or straight from GitHub (e.g. for an unreleased fix):
pip install git+https://github.com/naganumakr/safety-harness.git
Or for local development (editable, so edits to safety_harness/ take effect immediately):
git clone https://github.com/naganumakr/safety-harness.git
cd safety-harness
pip install -e .
python -m unittest discover -s tests -p "test_*.py"
Usage
from safety_harness import ActionSchemaRegistry, ActuatorGate, DecisionWatchdog
from safety_harness.adapters import FreezeInPlaceFallback, InMemoryLogger
from safety_harness.adapters.isaac_lab import (
IsaacLabCubeStackPerceptionAdapter,
IsaacLabCubeStackDynamicsAdapter,
)
from safety_harness.integrity import read_digest_file
cfg = "configs/example_action_schema.yaml"
# Pin the config to its known-good digest: a registry loaded without one blocks every action
# (config_integrity_verified), and one whose file doesn't match refuses to construct.
schema = ActionSchemaRegistry.from_yaml(cfg, expected_digest=read_digest_file(cfg + ".sha256"))
fallback = FreezeInPlaceFallback()
watchdog = DecisionWatchdog(fallback, deadline_s=0.2) # set from your real control cycle
gate = ActuatorGate(
perception=IsaacLabCubeStackPerceptionAdapter(...), # swap for your own stack's adapter
dynamics=IsaacLabCubeStackDynamicsAdapter(...),
fallback=fallback,
logger=InMemoryLogger(),
action_schema=schema,
watchdog=watchdog,
)
gate.gate(proposed_action) # -> Decision(verdict=PERMIT|BLOCK, ...), also fed to the watchdog
robot.execute(watchdog.command()) # the permitted action only while fresh and bit-identical; else freeze
Nothing here is Isaac-Lab-specific except the two adapter classes — swap those for adapters targeting your own robot stack and the engine, checks, and tests are unchanged. See "Contributing an adapter" below.
Security
Reporting a vulnerability (including a default-deny bypass — see the design doc's "NaN-Sensor Stress Test" for a real example of the kind of finding this covers), supported versions, and the coordinated-disclosure policy: SECURITY.md.
License
Apache License 2.0 — see LICENSE and NOTICE. The engine, the adapter interfaces, and the conformance test suite are all open under this license: the goal is for any robot maker to implement the four adapters for their own stack and run the same conformance suite against it, not to license the core code per-implementation. See the design doc's Release & Distribution section for the reasoning and the certification model this enables.
Contributing an adapter
- Implement
PerceptionAdapter,DynamicsAdapter,FallbackController, andLoggerfor your stack (seesafety_harness/adapters/isaac_lab.pyfor the reference shape). - Run the black-box and fuzz test suites against your adapter's
WorldState/PredictedTrajectoryoutput — they're written against the interfaces, not the Isaac Lab implementation, so they should run unmodified. - Open an issue or PR with your results. Passing the conformance suite is what "compliant adapter" means here — there's no separate certification process yet, but that's the intent (see the design doc's Roadmap).
Metadata
Release files for safety-harness 0.3.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| safety_harness-0.3.6.tar.gz | 126.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| safety_harness-0.3.6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 213.5 kB
Release files / safety_harness-0.3.6.tar.gz
| Download URL | safety_harness-0.3.6.tar.gz |
|---|---|
| Size | 126.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fcebb9d4284cc00ffb361a2160c46755d64e17942deeb1733d995d94036eb2e9
|
|
BLAKE2b-256 checksum How to use checksums |
32b38be76af5b8f546c725854a6e193a0301b46bdf70090e10d048cf63b0d2c4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.
Transparency logRelease files / safety_harness-0.3.6-py3-none-any.whl
| Download URL | safety_harness-0.3.6-py3-none-any.whl |
|---|---|
| Size | 86.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
eae83edcc8911bdf7199def3042d565ca8f1acb4c7037b04b443b4b49b596d66
|
|
BLAKE2b-256 checksum How to use checksums |
8eaecd8cdacc2780ae9db97fec2e3f1c711acb857d39ad259ffe5c3f6107a04a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.
Transparency log