Metadata-Version: 2.4
Name: fkyh
Version: 0.1.0
Summary: Reference implementation of the FK YH protocol: agent discovery, gossip headers, and independently verifiable attestations.
Author-email: cyber-bollox <341154877+cyber-bollox@users.noreply.github.com>
License-Expression: Apache-2.0
Project-URL: Repository, https://github.com/cyber-bollox/fk-yh
Project-URL: Issues, https://github.com/cyber-bollox/fk-yh/issues
Keywords: fk-yh,attestation,agent,mdns,prometheus,x402
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: signing
Requires-Dist: cryptography>=41; extra == "signing"
Provides-Extra: test
Requires-Dist: pytest>=8; extra == "test"
Requires-Dist: cryptography>=41; extra == "test"
Requires-Dist: prometheus_client>=0.20; extra == "test"
Requires-Dist: pyyaml>=6; extra == "test"
Dynamic: license-file

# 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](./SPEC.md)
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 **717 tests**:
**712 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`, `failed` and `inconclusive`
  are 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](./SPEC.md) | the protocol, 15 sections |
| [ATTESTATION.md](./ATTESTATION.md) | **the attestation protocol, v0.1 (normative)** |
| [STATUS.md](./STATUS.md) | **what is actually implemented, tested, partial, or absent** |
| [PLAN.md](./PLAN.md) | the build plan this repository was built to |
| [CONTRIBUTING.md](./CONTRIBUTING.md) | how to contribute, and how to run the suite |
| [SECURITY.md](./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](#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](#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-yh` at all;
- this repository's own reference node **does** advertise `_fk-yh._tcp`, so
  the browse command finds it while `examples/hello-world.py` or
  `examples/fk-yh-node/run_demo.sh` is 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:**

```bash
# 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.

```bash
# 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          # 717 tests collected, 712 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 `712 passed, 5 skipped` on macOS. Four of the skips
  are the live-mDNS tests: they need a real network and `FKYH_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, because `dns-sd` is 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 with `invalid_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:

```bash
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 specification
> - `fixtures/attestations/` — the conformance fixture set
>
> The 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.md`](PROVE-IT.md) is 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.
>
> ```bash
> 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`](./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 reach `evidence_missing`, `evidence_digest_mismatch` or
  `evidence_unreadable` at 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:

```bash
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](./STATUS.md#3-known-limitations).

| 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

```bash
python3 -m pip install -e '.[test]'

python3 -m pytest tests/ -q          # 717 tests collected, 712 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](./STATUS.md) for the full honest picture, including the known
limitations.

## Contributing

Read [CONTRIBUTING.md](./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](./.github/ISSUE_TEMPLATE/)
for a bug, a specification proposal and a conformance fixture. Security
problems go to [SECURITY.md](./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](./LICENSE).
