Sign Your First Policy Bundle
This walkthrough takes you through the authoring side of APF. You copy the demo bundle, change one rule, sign the bundle with a key you create, verify it offline, and load it into the policy service.
After completing it, you understand:
- How a bundle manifest binds every policy document by digest.
- How the key ID in the signature selects a trust root.
- Why every change needs a higher
policy_revision.
Prerequisites
- APF built from source and running as native processes, as described in Installation. The commands below use the variables from Set Up Each Terminal.
- A shell in the root of the APF repository.
Copy the Demo Bundle
Work on a copy so the committed demo bundle stays intact.
The bundle is a directory of policy documents plus a manifest template:
Change a Rule
Open $WORK/bundle/sandbox/default-policy.yaml. The httpbin entry allows only POST /anything at Layer 7. Add a second rule that also allows GET /get:
Increase the Revision
Open $WORK/bundle/demo-bundle.template.yaml and change policy_revision: 1 to policy_revision: 2.
The policy service already accepted the demo bundle at revision 1. Its anti-rollback counter rejects any bundle whose revision is lower, and rejects different content at the same revision. Every change you publish needs a higher revision.
Sign the Bundle
Sign the template with a new Ed25519 key. --generate-key creates the key file and a matching .pub file if the key does not exist yet.
The command hashes each manifest member, fills in the digests, and signs the result:
The key ID defaults to the key file’s stem, my-signer. It is written into the signature document at the end of the signed file:
Publish the Public Key as a Trust Root
Verifiers find the public key by matching the signature’s keyid to a .pub file stem in their trust-root directory. Keep the private key out of that directory.
Verify the Bundle Offline
Check the signature, every member digest, and the validity window before you ship the bundle:
Run the same command without --verify-only to also parse every member document. It prints one projection line, with kind, name, and size, for each surface it produced: the policy set and the provider profile set, which holds only ProviderProfile members, not Provider documents. The apf-compile CLI does not register the OpenShell projector, so it does not print the sandbox policy projection. The policy service produces that projection when it loads the bundle.
Load the Bundle into the Policy Service
Trust roots are read at startup, so restart the policy service with your trust-root directory and bundle. Reuse the same counter store so the anti-rollback check sees revision 1 from the earlier run. Stop both native services first, then start them again:
The document signing key stays apf-dev. It signs provenance tokens on served documents and is separate from the bundle signing key you created.
What the Signature Protects
The signature covers the manifest payload. The manifest lists each member file by path and SHA-256 digest of its bytes on disk. Together they make the bundle tamper-evident:
- Changing any member file changes its digest, and the policy service rejects the bundle with
manifest_hash_mismatch. - Changing the manifest invalidates the signature, and the policy service rejects the bundle with
signature_invalid. - Re-serving an older bundle fails the revision check with
counter_regression.
Try each of these in Reject a Tampered Bundle.
Next Steps
- To admit a sandbox under this bundle, continue with Your First Governed Agent.
- To learn every manifest field, refer to Bundle Structure.
- To use a production signing authority, refer to Configure a Signing Authority.