Security & Governance
The pyhall Skills & Tools system is built on the same WCP governance chain that governs all workers. Skills are not raw functions — they are governed artifacts with mandatory attestation, certification, and audit trails.
Core security model
Deny-by-default everywhere.
A skill can only execute when all of the following are satisfied:
- The canonical manifest is valid (hash verified)
- The adapter artifact is signed and its certification is present in the registry
- The worker is registered and passes attestation
- All required WCP controls are satisfied for the policy tier
- The namespace is authorized for the calling organization
- The skill is not on the ban list (org-level or global)
If any condition fails: execution is denied, a structured error is returned, and an audit event is written. There is no silent fallback.
Lifecycle controls
Every skill lifecycle event is auditable:
| Event | Audit record written |
|---|---|
skill init | Manifest created, author identity recorded |
skill build | Adapter generated, manifest hash recorded |
skill certify | Certification issued, signature attached |
skill publish | Registry entry created, artifact hash stored |
skill install | Installation recorded (if analytics enabled) |
skill execute | Execution decision recorded (ALLOW or DENY) |
skill deny | Denial reason, worker identity, timestamp |
Registry enforcement
The pyhall registry is consulted at enrollment, publish time, and every tool execution. There is no cached-trust path that skips governance.
When a skill is published:
- The manifest hash is stored immutably
- The adapter artifact hash is stored with its signature
- The certification timestamp and signer identity are recorded
- The revocation status is initialized to
active
When a skill executes:
- The registry is queried to verify the certification is still active
- The ban list is checked
- The attestation check is performed
- The governance gate decision is recorded
Kill switch
Every skill and every adapter supports an independent kill switch:
- Org-level revocation: an org admin can revoke a skill; all workers using it are denied immediately
- Global ban: the registry operator can ban a skill at the global level
- Adapter-level degradation: if a platform API breaks compatibility, the specific adapter is marked
degradedand denied
Kill switches take effect on the next execution gate call — there is no grace period for unauthorized execution.
Input validation
The pyhall compiler generates strict JSON Schema validators for all skill inputs. Manifests that produce malformed adapter output are rejected at build time — they cannot be published or installed.
Preventing silent bypass
pyhall is designed to fail loudly, not silently:
- Missing attestation callables → explicit deny + structured error
- Registry unreachable → fail closed (no execution allowed for uncertified installs)
- Missing controls → explicit deny
- Malformed adapter output → build fails, nothing is published
There is no “try anyway” path.
PolicyGate enforcement
Every skill invocation routed through make_decision() can require a policy gate evaluation. The gate returns one of three decisions:
| Decision | Effect |
|---|---|
ALLOW | Execution proceeds |
DENY | Execution denied, audit record written |
REQUIRE_HUMAN | Execution paused pending human review |
When escalation.policy_gate=True in a routing rule, a policy_gate_eval callable must be provided to make_decision(). Missing the callable on a gate-required rule produces a denial — there is no default-allow fallback.
from pyhall import make_decision, PolicyGate
class MyGate(PolicyGate): def evaluate(self, context): if context["env"] == "prod" and context["data_label"] == "RESTRICTED": return ("REQUIRE_HUMAN", "policy.v1", "restricted_data_in_prod") return ("ALLOW", "policy.v1", "default_allow")
gate = MyGate()decision = make_decision(inp, rules, ..., policy_gate_eval=gate.evaluate)Fail-closed behavior
pyhall is designed to fail loudly, not silently. Failure modes and their outcomes:
| Failure | Result |
|---|---|
| No routing rule matches | Denied (matched_rule_id = "NO_MATCH") |
| Required controls missing from registry | Denied |
policy_gate=True but no policy_gate_eval provided | Denied |
| Worker not enrolled in registry | Denied |
| Attestation hash mismatch | Denied (worker_attestation_valid = False) |
| Manifest missing or unreadable | ATTEST_MANIFEST_MISSING deny code |
| Package tampered (hash mismatch) | ATTEST_HASH_MISMATCH deny code |
| HMAC signature invalid | ATTEST_SIG_INVALID deny code |
There is no silent fallback path. Every denial emits a structured audit event.
WCP_ATTEST_HMAC_KEY for production
Worker package attestation is signed with HMAC-SHA256 using a secret read from the WCP_ATTEST_HMAC_KEY environment variable.
export WCP_ATTEST_HMAC_KEY="your-namespace-signing-secret"Set this in your CI/CD environment at build time (for build_manifest()) and on your production worker host (for PackageAttestationVerifier.verify()). The same key must be present at both points — build and runtime.
If the env var is not set at verify time, PackageAttestationVerifier.verify() returns ATTEST_SIGNATURE_MISSING and denies execution.
For hardened production deployments, rotate this key periodically and re-attest all worker packages. The asymmetric (Ed25519) signing path is planned for a future release.
WCP alignment
The Skills governance model maps directly to WCP control model fields:
| Manifest field | WCP control |
|---|---|
namespace | Org-level authority scope |
policy_tier | Risk tier enforcement |
capabilities | Capability declaration |
attestation_required | Worker attestation gate |
audit_events | Immutable audit trail |
See the WCP specification for the full control model.