FK YH — The VBI for AI Agents
An open protocol for agent discovery, coordination, observability, and independently verifiable attestations. Piggybacks on existing protocol fields (mDNS, HTTP headers, Prometheus). No new infrastructure.
FK YH is this protocol's name. The name is not expanded here — this specification claims no expansion for it.
VBI is the "vertical blanking interval" — the unused scanlines the BBC filled with Ceefax data in the 1970s. FK YH is the same trick for agents: the data rides in fields the protocols already have. SPEC.md §1.2 explains the analogy properly.
What FK YH is for. An agent publishes what it is doing in HTTP headers and on the local network. A third party — one who has never seen the agent and need not trust it — can check a signed statement about whether the work actually happened.
The answer is three-valued, and it is honest about its own uncertainty:
| Outcome | Meaning | May the consumer act? |
|---|---|---|
verified |
the evidence positively confirms the expected outcome under policy_id |
yes |
failed |
the evidence contradicts the expected outcome | no |
inconclusive |
the evidence is missing, stale, inaccessible, or insufficient to reach a defensible conclusion | no |
Only verified is actionable. An authentic failed or inconclusive is
still an authentic statement: authenticity and success are different
facts, and the protocol reports them separately (ATTESTATION.md §5).
Where the project is (2026-10-22). The protocol and the attestation
specification are v0.1. This repository is private while the
protocol is being finalised. The LAM is
private — staged for public release, and not published by this
release. The suite collects 722 tests:
717 pass, 5 skip on macOS with the test extra installed. The last
CI run on this branch was green in every job — seven job entries,
because the Python package job runs the whole suite on both 3.11 and 3.12.
What this repository is not
Up front, because a trust protocol that overstates itself is worse than no protocol at all.
- It does not prove that the underlying claim is true. A signature proves provenance and integrity — that this statement, from this key, has not been altered. Whether the world actually matches the statement needs reliable evidence, a sound method and explicit assumptions (ATTESTATION.md §9). A verifier that signs a false conclusion is indistinguishable, by design, from one that signs a true one.
- It is not a status protocol.
verified,failedandinconclusiveare three distinct facts. "Authentic" and "successful" must never be collapsed into one another. - It is not implemented end to end. x402 payments (§6) are specified, not implemented, by decision — there is no x402 code here and nothing has been settled on chain. The 5-stage guarded executor is the reference implementation's, not the protocol's, and the Python half of it is not in this repository.
- It does not make discovery trustworthy. mDNS advertisement is unauthenticated by design (SPEC.md §8.1). A deployment that needs authenticated discovery has to add it.
- It has not been implemented by an outsider. The three consumers below are all by the same author. That proves the protocol interoperates across languages; it does not prove an outsider can implement it from the document alone. That test has not been run.
Why
The current generation of agents conflate saying success with being successful. They log "done" without independently checking the post-state. The attestation layer is the part of FK YH that addresses this: a verification result becomes a portable, signed statement that a third party — one who has never seen the agent or the verifier's code — can check for themselves.
The 5-stage guarded executor that produces those results belongs to the reference implementation, not to the protocol.
This repository
| SPEC.md | the protocol, 15 sections |
| ATTESTATION.md | the attestation protocol, v0.1 (normative) |
| STATUS.md | what is actually implemented, tested, partial, or absent |
| PLAN.md | the build plan this repository was built to |
| CONTRIBUTING.md | how to contribute, and how to run the suite |
| SECURITY.md | how to report a security problem |
pyproject.toml |
the package metadata: Python 3.11 or newer, cryptography as an optional extra (pip install -e '.[signing]') |
fkyh/ |
the reference implementation — standard library only. Every module is listed under The fkyh package. |
fkyh/signing.py |
optional signed gossip headers (§4.4) — cryptography stays optional |
fkyh/keys.py |
the key store: keys on disk, revocation, rotation, trust configuration |
fkyh/servicetype.py |
the RFC 6335 service-type naming rules, the derived types and the profile registry (§3.1) |
tests/ |
the test suite, including a spec-conformance test |
fixtures/attestations/ |
signed conformance fixtures + manifest |
examples/fk-yh-node/ |
a runnable node: announce, answer, measure, prove |
examples/independent-consumer/ |
a second consumer, in Python, written from the spec alone — by the same author as the implementation; see the disclosure below |
examples/attestation-consumer-go/ |
a third consumer, in Go, written from the spec alone — also by the same author |
examples/go-reference/ |
the Go executor implementation, and the cross-language oracle |
examples/grafana/ |
the reference Grafana dashboard, with the provisioning that loads it |
contracts/fkyh-registry/ |
an optional on-chain agent registry (Soroban) — §8.3. Verifies Ed25519 on chain, so a §4.4 signature can be checked against a registered key. Optional: no registry is required, and a node using none is conformant. |
fixtures/x402/, fixtures/stellar/, fixtures/jcs/, fixtures/rfc/ |
the vendored oracles the pins are checked against, with SHA-256s in fixtures/ORACLES.json — the x402 and Stellar specifications, the RFC 8785 (JCS) reference test vectors, and the RFC texts themselves |
The reference implementation, and one thing that will surprise you
The reference implementation — the LAM — is a separate repository, and it
is private — staged for public release, and not published by this
release: github.com/cyber-bollox/lam. There is no public URL for the LAM
yet, and no document in this repository prints a clickable link that
404s: ask for access to the LAM and you will be given a URL that
works. (This repository — FK YH — is private too; see
Get started.)
The LAM adds a 5-stage guarded execution pipeline (Prepare → Authorize → Execute → Verify → Commit), per-action independent verifiers, layered memory, pattern discovery, and a self-evaluation harness. Those are properties of that implementation. The protocol does not specify them, so an implementer of FK YH is not required to build them.
The distinction matters: this repository is the specification, and a reference implementation of the parts that are specified. The executor surfaces are the LAM's, and the LAM is where the Python reference for the 5-stage guard lives.
⚠️ The browse commands in the specification return nothing against the
reference implementation. SPEC.md §13.2 tells an operator to run
dns-sd -B _fk-yh._tcp local to find agents on the network. Against the
LAM those commands print nothing, and it is not your mistake:
- the LAM advertises itself as
_lamfax._tcp, not_fk-yh._tcp— it contains no occurrence of_fk-yhat all; - this repository's own reference node does advertise
_fk-yh._tcp, so the browse command finds it whileexamples/hello-world.pyorexamples/fk-yh-node/run_demo.shis running.
So: browse _fk-yh._tcp for FK YH agents, _lamfax._tcp for the LAM's
own. The protocol's service type is _fk-yh._tcp (§3.1); the LAM had not
adopted it when this was written.
Get started
Install the package:
# the library and the `fkyh` command
python3 -m pip install fkyh
# attestation signatures need `cryptography`, which is an optional
# extra on purpose: the library itself is standard-library only
python3 -m pip install 'fkyh[signing]'
The source repository (cyber-bollox/fk-yh) is private — the clone
URL returns 404 for anyone who has not been invited, so ask for
access to cyber-bollox/fk-yh if you want the tree, the audit
reports, or the history.
You do not need the tree to use the package. pip install fkyh gives
you the library and the fkyh command. The conformance corpus —
the signed fixtures, the manifest, the oracles, and the suite that
checks them — ships in the source distribution, so
pip download --no-binary :all: fkyh gives you the corpus too.
Once you have the tree, everything below runs from the repository root with nothing else to install but two packages.
# Python 3.11 or newer — the floor pyproject.toml declares, and the
# version CI runs the suite on. 3.12 is the one most people have.
python3 --version
# the package, plus the test extra (which carries the optional
# prometheus_client and pyyaml the suite needs), plus the runner
python3 -m pip install -e '.[test]'
python3 -m pytest tests/ -q # 722 tests collected, 717 pass, 5 skip
# the whole protocol, end to end
python3 examples/hello-world.py
# a second consumer, written from the specification alone
python3 examples/independent-consumer/run_conformance.py
Not installing anything? The package imports from the repository root as it
stands — python3 -m pip install -e '.[test]' and run everything from
this directory is enough.
What you should see:
- pytest ends
717 passed, 5 skippedon macOS. Four of the skips are the live-mDNS tests: they need a real network andFKYH_AUDIT_LIVE=1, so they skip unless you ask for them. CI runs them — the macOS job has a step of its own with the variable set, because a pin that never runs cannot fail, and one of these four was found RED the first time it was actually executed (2026-10-22), still asserting the behaviour a fix had already removed. The fifth is the publication-truth pin, which stands down while this repository is private — the claim it polices ("this repository is private") would be true, and flagging it would be a lie. On Linux two more groups skip, becausedns-sdis macOS-only. Nothing is hidden behind them, and the skip reasons are printed by the suite. - hello-world.py starts a node, advertises it on mDNS as
_fk-yh._tcp.local, reads back its own headers and metrics, and then has a separate party validate its attestation — including a tampered copy, which is rejected withinvalid_signature. - run_conformance.py ends
20/20 conformance checks passed— one per fixture in the manifest (18 of them), plus the two steps of the replay sequence.
The demo, and then the loop. examples/fk-yh-node/run_demo.sh
starts a node and reads every surface it exposes. Its §8 line reads
unavailable: HTTP Error 503, and that is correct: a node with no
attestation supplied refuses to invent one, because it must not sign its
own state. The demo says so itself, and points at the script that does
exercise the path. For the whole loop — a separate program with its
own key verifying the node and signing the result — run:
cd examples/fk-yh-node && ./run_independent_loop.sh
That prints verdict : ok / verified and trusted : True, and it is
the demo worth reading: the node did not sign this, a separate party did.
Implement this: independent attestation consumer
The attestation specification is written to be implementable from the
document alone. ATTESTATION.md is the normative text;
fixtures/attestations/ is the conformance corpus.
With access to this repository, take two things and nothing else:
ATTESTATION.md— the specificationfixtures/attestations/— the conformance fixture setThe point of the exercise is to build a consumer without reading the implementation, so take only those two things. The fixture set ships inside this repository, so it comes with the repository — there is no separate copy to fetch.
PROVE-IT.mdis the full ask: what to build, what a report must contain, how to run each check, and what a pass means and what a failure means. A failure is as useful as a pass — it is a bug report against the specification, and it is wanted.ls fixtures/attestations/
Read this before you start: the mode the corpus runs in
The corpus reaches every code §7.1 publishes (ATTESTATION.md §10.2):
all 15, including revoked_verifier, unauthorised_policy,
not_yet_valid, attempt_mismatch and the three evidence codes. A
consumer that never emits revoked_verifier, never checks the attempt
binding and never re-derives an evidence digest now fails the fixture
set. The manifest is the authority: fixtures/attestations/manifest.json
names the expected code for every fixture, and ATTESTATION.md §10.2 says
the same thing in prose. To go from one error code to the fixture that
exercises it, read
fixtures/attestations/MANIFEST-COVERAGE.md.
It reaches them in a mode, and the mode is part of the contract. Give your consumer:
- the published evidence directory,
fixtures/attestations/evidence/— so step 9 re-derives the digests instead of trusting the recorded ones. A consumer that cannot reach the evidence has nothing to refuse, and cannot reachevidence_missing,evidence_digest_mismatchorevidence_unreadableat all; - the task and the attempt the manifest records (
task_id,attempt_id) — so steps 6 and 7 are checked; - a fresh replay store per fixture, and one shared within a sequence — §10.1 says why, and the published runners do it.
The published runner shows the whole shape:
python3 examples/independent-consumer/run_conformance.py
The fixtures, and what your consumer must return
Your consumer must produce the verdict recorded in manifest.json for
every fixture:
| Fixture | Expected |
|---|---|
valid-verified.json |
accepted, verified |
valid-failed.json |
accepted, failed — accepted but not successful |
valid-inconclusive.json |
accepted, inconclusive |
evidence-no-digest.json |
accepted, inconclusive — the evidence record omits sha256 |
evidence-null-digest.json |
rejected — malformed — the evidence record carries "sha256": null |
missing-signature.json |
rejected — missing_signature — the signature member is absent |
tampered.json |
rejected — invalid_signature |
unauthorized-verifier.json |
rejected — unknown_verifier |
revoked-verifier.json |
rejected — revoked_verifier — signed by a key trust.json revokes |
unauthorized-policy.json |
rejected — unauthorised_policy — signed by the trusted key, for a policy it may not attest under |
expired.json |
rejected — expired |
not-yet-valid.json |
rejected — not_yet_valid — issued_at is in the future |
wrong-task.json |
rejected — task_mismatch |
wrong-attempt.json |
rejected — attempt_mismatch — a valid attestation for another attempt |
evidence-missing.json |
rejected — evidence_missing — the evidence file is not published |
evidence-digest-mismatch.json |
rejected — evidence_digest_mismatch — the file's digest is not the recorded one |
evidence-unreadable.json |
rejected — evidence_unreadable — the reference escapes the evidence root |
unsupported-version.json |
rejected — unsupported_version |
A rejection reports a code and no outcome; an acceptance reports
ok and the attestation's own outcome. inconclusive is the verifier's
statement, so a consumer never synthesises one for itself (ATTESTATION.md
§5, §10.2).
Plus one sequence: present valid-verified.json twice to a consumer that
keeps replay state. The second presentation must be rejected with replay.
Replay is consumer state, not a property of the message — see
ATTESTATION.md §8 and the sequences array in the manifest.
Plus one artifact: section-4-3-worked-example.json is the object
ATTESTATION.md §4.3 publishes, so that the exact canonical bytes the
specification prints are pinned against the code. It is not a verdict
fixture, and the manifest lists it under artifacts.
The hard part is §4.1: the canonicalisation rule. Get it byte-identical and
everything else follows. §4.1 now cites JCS (RFC 8785) and this
repository implements it in fkyh/jcs.py, so the rule is a standard
rather than a second copy of itself; the JCS reference test vectors are
vendored under fixtures/jcs/ and the RFC texts under fixtures/rfc/, so
the implementation is checked against the standard's own bytes.
Two details an implementer gets wrong easily:
- the key id is derived, not chosen:
key_id = "ed25519-" + hex(sha256(raw_public_key_bytes))[:16]; - the trust configuration is a separate, signed-key material file (ATTESTATION.md §6), and a consumer MUST consult it before acting.
Three implementations, two languages, one author. The Go consumer is the one that matters: two Python implementations can share a misunderstanding and never find it, because they share a language, a JSON library and a set of habits. Go shares none of those.
All three were written by the same author, so this proves the protocol interoperates — it does not yet prove an outsider can implement it from the document alone. That test has not been run. See STATUS.md.
| Implementation | Language | Score |
|---|---|---|
fkyh/attestation.py |
Python | 20/20 |
examples/independent-consumer/ |
Python | 20/20 |
examples/attestation-consumer-go/ |
Go | 20/20 |
The Go test suite pins the canonical payload as a hardcoded prefix — the
bytes from {"alg":"ed25519", up to and including "verifier":{"key_id":".
That is every field of the attestation except the key id's own value (the
signature is not part of the canonical bytes at all). If the two languages
ever disagree about key ordering, escaping or number formatting inside that
prefix, it fails loudly rather than silently.
Running everything
python3 -m pip install -e '.[test]'
python3 -m pytest tests/ -q # 722 tests collected, 717 pass, 5 skip
python3 examples/hello-world.py # the protocol, end to end
cd examples/fk-yh-node && ./run_demo.sh
cd examples/fk-yh-node && ./run_independent_loop.sh # the full attestation loop
python3 examples/independent-consumer/run_conformance.py
# the cross-language consumer
cd examples/attestation-consumer-go
go build -o /tmp/attestation-consumer .
/tmp/attestation-consumer --conformance ../../fixtures/attestations
go test ./...
The fkyh package
| Module | Spec | What |
|---|---|---|
fkyh/__init__.py |
— | the package surface: PROTOCOL_VERSION and the state types |
fkyh/state.py |
§4 | agent and fleet state, and the response headers |
fkyh/discovery.py |
§3 | mDNS advertisement and browsing, TXT encoding |
fkyh/servicetype.py |
§3.1 | the service type, the RFC 6335 naming rules, the derived types, the profile registry |
fkyh/metrics.py |
§5 | Prometheus exposition, plus a parser for it |
fkyh/gossip.py |
§7 | the fleet scraper and the observer |
fkyh/jcs.py |
ATTESTATION §4.1 | JCS (RFC 8785) canonicalisation — the bytes that get signed |
fkyh/signing.py |
§4.4 | signed gossip headers — the canonical bytes, the signature header, the verdict vocabulary |
fkyh/attestation.py |
ATTESTATION §7 | the consumer side of the attestation protocol |
fkyh/signer.py |
ATTESTATION | the issuer side |
fkyh/keys.py |
ATTESTATION §6, §4.4 | the key store: keys on disk, revocation, rotation, trust configuration |
fkyh/node.py |
— | the HTTP service that serves all of it |
fkyh/__main__.py |
— | the command line: python3 -m fkyh keys create|show|revoke|rotate --keystore <dir>, and python3 -m fkyh verify --attestation <file> --trust <file> |
Standard library only. cryptography is optional, and it is needed for two
things: attestation signatures (fkyh/signer.py, fkyh/attestation.py)
and signed gossip headers (§4.4 — fkyh/signing.py, fkyh/keys.py).
A node that signs neither works without it. Nothing here imports the LAM
— a test enforces it, because a protocol that is really specified by one
implementation is not a protocol.
The fkyh package's own version and the protocol version are
different things: fkyh.__version__ is the package version, and
fkyh.state.PROTOCOL_VERSION is the protocol version — "0.1". The
attestation envelope's schema_version is "0.1" as well
(fkyh/attestation.py); a consumer MUST reject a version it does not
understand rather than guess (ATTESTATION.md §11).
The spec-conformance test
tests/test_spec_conformance.py parses SPEC.md and ATTESTATION.md and
asserts the code matches what they say: the header names, the metric
names, the error-code vocabulary, the service type, the outcome set.
This is the test that keeps the protocol honest. It caught a real divergence — the two consumer implementations disagreed on eleven error codes, and the reference implementation did not match the spec's own list.
Status
v0.1 (draft). Sections 1–15 are written, and the attestation protocol v0.1 is specified with a published conformance fixture set.
The Python reference implementation of the 5-stage guarded executor lives in
the LAM repository. The Go reference (examples/go-reference/) is the
cross-language oracle for it. This repository cannot reach the LAM
repository from CI, so what CI checks here is the oracle against the
pinned contract in fixtures/executor/conformance.json — the same action
set and the same expected stage, status and verifier outcome that the
LAM's lam_conformance.py compares the two implementations against. Both
sides are held to one document, and neither can drift from it silently.
x402 is specified but not implemented, by decision. See STATUS.md for the full honest picture, including the known limitations.
Contributing
Read CONTRIBUTING.md first. The short version: the
suite is the gate — python3 -m pytest tests/ -q must be green — and a
change to a specification document must be checked against the code, not
merely written down. There are issue templates
for a bug, a specification proposal and a conformance fixture. Security
problems go to SECURITY.md, not to the public issue
tracker.
Design partners
We are looking for teams building multi-agent systems, agent observability, or trust infrastructure who want to test independent verification against a real workflow. Open an issue describing your use case.
License
Apache License 2.0, © 2026 Jody Wicks. Chosen over MIT for the patent grant, which matters for a protocol others will build on. See LICENSE.
Metadata
Release files for fkyh 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| fkyh-0.1.1.tar.gz | 659.0 kB | Without provenance | Details for fkyh-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| fkyh-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for fkyh-0.1.1-py3-none-any.whl |
Total release size: 733.2 kB
Release files / fkyh-0.1.1.tar.gz
| Download URL | fkyh-0.1.1.tar.gz |
|---|---|
| Size | 659.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a871f7ca1c88db121e9545bf319da072aaa8a0bda9f10472c2a01860b1036718
|
|
BLAKE2b-256 checksum How to use checksums |
18ea4c5a120dbb887c54bffce05606cf874c1ef5f7fec4b7528b159aa403bd3f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.4
|
Release files / fkyh-0.1.1-py3-none-any.whl
| Download URL | fkyh-0.1.1-py3-none-any.whl |
|---|---|
| Size | 74.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
98c94aa4751447bbb7c3d85caf985c4e9ee63b9b5b27c220ca941c7cb891f6ea
|
|
BLAKE2b-256 checksum How to use checksums |
9eacd7b177b10d88874ca5b2eb29f446b654e069e5ab00a010c006ec8e81e87f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.4
|
Security for fkyh 0.1.1
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.