Back to blog
·by Patrick Hofmann

The Spec Follows the Code

I wanted to write a protocol for cross-SP data access and then build against it. It came out the other way around: first implemented and audited across five service providers, then reconciled §4/§5/§6 with what had proven correct. The new IdP mechanism the spec assumed was already there — just shaped differently.

OpenApeArchitectureInfrastructureBuilding in Public

I had written a protocol. SP Data Access Profile: how a service provider fetches data from another, in a human's name, over delegated grants. DDISA trust doctrine, scope catalog, a section on delegation, one on standing, one on consume. Cleanly numbered, §1 to §6. The plan was: spec stands, now we build against it.

The building rewrote the spec. Not because I specced sloppily, but because three assumptions tipped over on the first real run. In the end I reconciled §4, §5 and §6 with what the implementation had already proven. No new IdP mechanism. The spec ran after the code, and here that wasn't the failure — it was the more honest way.

What the spec assumed

The spec assumed that for delegated cross-SP access a new mechanism in the identity provider is needed. §4 described how a delegation arises at the IdP. §5 described how the target SP checks the delegated scopes — offline, from the token itself, because the token carries the scopes inline. §6 described consume and revoke as something I'd still have to build.

Three assumptions, all plausible on paper. All three didn't survive the first smoke.

What the implementation showed

The same violation, in every SP

Before any delegated path could work, the issuer resolution had to be right. Every SP verifies incoming tokens against an issuer. In all five SPs — timetrack, tasks, plans, preview, chat — the issuer stood hardcoded in the code:

// before, the same line in every SP
const ISSUER = "https://id.openape.ai";
verifyJWT(token, { issuer: ISSUER });

That's against its own DDISA doctrine. Which IdP is authoritative for a subject stands in the _ddisa record of the subject domain — not in a constant in the SP. The fix:

// after: issuer from the subject domain's _ddisa TXT record,
// not from a constant
verifyJWT(token, { issuer: issuerFromSubjectDdisa });

Behavior-preserving, because hofmann.eco points to id.openape.ai via _ddisa anyway — the effect is identical today. But the violation was uniform. Five SPs, the same wrong line — copied, not decided anew each time. That's exactly the kind of finding an audit yields and a spec doesn't: not what should hold, but what actually holds, equally wrong everywhere. That became §2.1.

The mechanism was already there

§4 and §6 described what I'd still have to build in the free-idp — delegation, standing grants, consume, revoke. I did an audit before the rebuild, deliberately, because an intervention in the production IdP has a large blast radius. The audit showed: it's all already fully there, in the IdP auth code and in @openape/grants. Delegation, standing, consume, revoke — done, tested, in production.

The M5 risk I had standing in the plan as "risky IdP rebuild" was unfounded. M5 wasn't a rebuild. M5 was docs — align the spec with a mechanism that already stood. The actual work was SP-side in M4. Had I continued spec-first, I'd have built an IdP mechanism that already existed, only shaped differently than my spec had imagined it.

The delegation token looks different than expected

§5 assumed the target SP could check delegated scopes offline, because they stand inline in the token. Real end-to-end refuted that: the delegation authZ JWT carries no inline scopes. It carries a grant_id reference, and the aud is the target SP, not apes-cli. The spec's offline-scope assumption doesn't hold — the grant has to be dereferenced, the scopes don't live in the token.

The audit wouldn't have found that. Audit-first prevented the unnecessary IdP rebuild; but that the token form doesn't fit the spec only shows when a real request goes through all layers. Two different tools, two different classes of error.

How §5 looks now

Instead of offline scope validation, §5 now describes a chokepoint: every method maps to a scope, the subset check happens at the token exchange, the token has a short TTL. The SP doesn't ask the token "which scopes do you have", it asks "may this method with this grant". That's not what I'd written in the spec. It's what proved to be the right place for the boundary while building.

The same logic landed in every SP as the same sequence of commits: resolve issuer from DDISA (§2.1), declare scope catalog in /.well-known/openape.json (§3), enforce delegated scopes at the chokepoint (§5). Five times the same order, because the spec in the end described what the five SPs already did.

What fell away

The section "new IdP mechanism". There is none. §4 and §6 now reference delegation, standing and consume as they really work, instead of describing a mechanism I'd have built had I not looked first.

The offline-scope assumption from §5. Struck, replaced by the chokepoint.

The commit that records it says it dryly: reconcile SP Data Access §4/§5/§6 with implemented delegation/standing/consume (no new IdP mechanism). The "no new IdP mechanism" is the whole point in four words.

Spec-first is the sermon

Everyone preaches spec-first. Spec stands, then you build against it, the code can't deviate from the truth because the spec is the truth. That's good discipline for interfaces where several parties build at the same time and no one may break the other.

Here exactly one party built, and the spec was a hypothesis about how a system would work that didn't exist yet. A hypothesis wrong in three places: once because a mechanism already existed; once because a violation ran uniformly through five codebases; once because the token form didn't fit the assumption. Had I treated the spec as truth, I'd have built against three wrong assumptions.

A spec that describes what demonstrably runs is more trustworthy than a spec that prescribes what should run. The first is a record of facts. The second is a bet. I didn't plan to let the spec follow the code — I planned to do it the other way around. But when the code shows you your spec is wrong in three places, then correcting the spec isn't a defeat. It's the only thing that makes the spec worth anything at all.


Code: github.com/openape-ai/openape, MIT-licensed. The SP Data Access Profile lives in the protocol repo, the issuer fix (§2.1) and the scope chokepoint (§5) in each SP individually.