Identity, attestation, and the trust chain
Normative where marked. Attestation formats are the least settled part of this standard and need vendor input before ratification.
Every result carries an evidence trail: player, facility, bay, device, software, settings, round, shot. This chapter says what each link asserts, who signs it, and what happens when a link cannot be proven. How far a result can be trusted comes from the narrower chain in §2.3.
Trust at a glance#
A final record is only as trusted as the lowest level in its chain: the launch monitor, the simulator, any sensor that changed the result, and the bay. One weak link sets the level of the whole record.
| level | a device or simulator gets here when… | a bay gets here when… | typically enough for |
|---|---|---|---|
A0 | anyone builds on the open standard; no agreement | nobody has claimed it | play, personal statistics |
A1 | its maker accepts the Technology Partner Agreement — report truthfully, never build a feature that lets a player cheat, report any way found to do either — and publishes a digest of each release that SGA verifies | its owner claims it under the Bay Owner Terms or the Bay Facility & Certification Agreement | the SGA Player Rating, leagues |
A2 | plus it signs what it sends, with a registered key kept in firmware or a protected executable | it is certified at a tier requiring A2 (14) | sanctioned events, the SGA Certified mark |
A3 | plus the key is in hardware nobody can extract | it is certified at a tier requiring A3 | championships |
Each step up adds proof that a component is what it claims to be. Events may be run by bay owners, simulator partners, other organizations, charities and SGA; each states the lowest level it accepts (08).
Three other things are not levels, and an event can ask for each separately: provenance (01), how each number was obtained; vendor assurance (§2.1), whether the running build is the one its maker published; and identity assurance (08 §4), how sure SGA is who is swinging.
1. What can and cannot be proven#
| claim | provable how | strength |
|---|---|---|
| This firmware build is what the vendor published | vendor signature over the build | strong, if the vendor participates |
| This device holds the private key for the public key its maker registered with SGA | challenge-response over a session nonce | strong |
| This key is in tamper-resistant hardware | secure element attestation | strong, hardware-dependent |
| This device is physically in this bay | bay pairing token from the placard | weak alone — the placard is static, so it proves someone was at the bay once, not that they are now |
| This device is in this bay right now | rotating proximity token, phone advertising, engine scanning (§4.2) | moderate — proves a phone stayed through the round |
| This data came from that sensor, not a replay | signed, nonce-bound, monotonic sequence | moderate to strong |
| The person swinging is the member | out of scope here — QR, biometric, camera | weak alone, layered by the platform |
A software-only device cannot prove where its numbers came from. If the vendor's own application signs its output with a key stored on the same PC an attacker controls, a determined attacker extracts the key — whatever the vendor does. That is not a flaw in this design; it is a property of the platform.
The standard's response is not to pretend otherwise. It is to grade the claim: declare the attestation strength, record it, and let the competition rules decide what a given tier is good enough for. A recreational round and a national championship should not require the same assurance, and pretending they do just means the championship tier is a lie.
2. Attestation tiers#
A tier says what a round is good for. A0 and A1 differ in who stands behind the product; A2 and A3 add a signature and differ in where its key is kept. A tier belongs to one component; a result takes the lowest tier in its whole chain — simulator, launch monitor, every result-modifying sensor and the bay itself (§2.3).
| tier | what it takes | hardware device | simulator software | suitable for |
|---|---|---|---|---|
A0 | nothing: the standard and the engine are open, free and unencumbered | no key | no key | recreational only |
A1 | the Technology Partner Agreement, and digests published for SGA to check | firmware version verified against a published digest | software version verified against a published digest | verified rounds, rating, leagues |
A2 | the partner agreement and digests, plus the signing endorsement and a signing certificate whose key is kept somewhere reasonable | key in firmware or NVRAM | key inside the executable, not trivially extractable and the method attested | sanctioned competition |
A3 | the partner agreement and digests, plus the signing endorsement and a signing certificate whose key is in hardware others cannot reach | on-device secure element or TPM | key on a hardware token | championship |
Nothing is needed to use SGA-OIS. Anyone may implement the standard, run the engine and ship a product that speaks it, without an account or an agreement. What they produce is fully usable, at A0.
The Technology Partner Agreement and published digests are the floor for the three tiers above, not a step that A2 and A3 pass through on the way up. What A2 and A3 add is signing: an endorsement beyond the partner agreement, a signing certificate, and a declared place to keep its private key. The jump from A1 to A2 is therefore "you now sign what you send"; the jump from A2 to A3 is "and the key is somewhere nobody can reach it."
A1 is the line that matters commercially, because it is where a round starts counting toward a player rating. It is also the first rung that requires an agreement — the Technology Partner Agreement, which carries no certification requirements but does bind the partner to report truthfully and never to build a feature that lets a player cheat. That commitment, plus a build the engine can measure against a published digest, is what A1 rests on.
Hardware and software can both reach every level. For simulator software, A3 means a hardware token at every installation — a key under this standard signs every shot at every bay, not a build once — so A2 is the realistic ceiling for software in the field, and A3 is available where a venue will bear that cost. A2 is the realistic target for the first cooperating manufacturers; A3 is where the standard is ready to go, not where it starts.
2.0a What "not trivially extractable" has to mean#
The software A2 rung is the one that decays if it is left vague. A key pasted into a string literal is inside the executable and no better than the file it replaced — and a rung that the least effort in the room satisfies is not a rung.
So A2 for software requires that the key not be recoverable by trivial static extraction, and that the vendor attest to the method used. Code signing the binary is the usual way there and is not mandatory: a great many simulators began as independent projects and some still are, and excluding them would cost the ecosystem more than the rung is worth. What is not acceptable is a plain constant.
The tier is a declared and tested property: the conformance kit determines what a device can actually demonstrate, and a device may not claim a tier above what it demonstrates. Same rule as provenance, same reason.
2.1 Vendor assurance — a second axis, not a higher tier#
The tier table says who stands behind a product and how its key is held. It says nothing about whether the party holding the key is accountable for what it signs.
That distinction is not academic. A signature proves origin and integrity — the same key signed this, and nothing changed afterward. It does not prove truthfulness. A vendor can label a modeled value measured and sign it with an impeccable A3 key, and every cryptographic check passes. Provenance labels are self-asserted at every tier, which is the gap this section closes.
So assurance is a separate, orthogonal dimension:
vendor_assurance | means | how it is earned |
|---|---|---|
none | nobody has warranted anything. The default, and where a product sits until its maker is a Technology Partner publishing digests. | — |
verified_build | a named party has contractually attested that what this product reports is factual, and the software actually running has been measured and matches a build that party published | contract, plus a measurement the engine takes itself |
certified | everything verified_build requires, and the release holds the SGA Certified mark: it passes the conformance suite and signs at A2 or higher, verified by SGA | contract, measurement, conformance and a verified signature |
The ladder is cumulative, and certified is the top of it. Both levels rest on the same contractual commitment, the Technology Partner Agreement's rule that a value labeled measured was measured, because that is the gap a signature cannot close and no amount of key custody ever will.
What separates them is who has to do something. verified_build is mechanical and largely self-service: ship an image the engine can hash, and the engine proves for itself that the software running is the software published. certified additionally requires the release to hold the SGA Certified mark (Governance). That is why it sits above, and why it cannot be obtained simply by publishing a build.
Key custody and accountability are different questions. A vendor whose key sits in a secure element can still label a modeled value measured: the signature will be perfect and the claim false. A signature proves origin and integrity, never what a number means. So an event can ask for each separately:
min_attestation_tier: "A2" -> the equipment can prove itself
min_vendor_assurance: "verified_build" -> a named party is accountable for it,
and the running software is the
software they published2.2 verified_build, and who does the measuring#
For hardware, firmware_digest is self-reported inside the signed identity (§3) and that is acceptable: at A2 the key sits in the device, and at A3 a secure element measures what is actually running. Key custody is what makes a false claim expensive to make.
For software the same arrangement is circular. A modified build can report the published digest as easily as the genuine one, and a key in software can be extracted to sign the report. The digest ends up attesting to itself.
So verified_build requires a measurement the vendor does not supply:
- The engine determines which binary is running, by resolving the peer of the session's own transport connection to a process image on the host. It MUST NOT take a path the participant declared for this purpose.
- The engine hashes that image and records the hash. The server compares it with the vendor's published releases.
- The engine writes its own measurement into the journal, attributed to the engine, alongside anything the participant claimed.
- A participant MAY declare its binary path. That declaration is a cross-check and a diagnostic, never a trust anchor — a declared path that disagrees with the observed one is itself evidence, and both are recorded.
A vendor claiming verified_build MUST ship an image the engine can hash: at a stable path, and not packed or obfuscated in a way that defeats measurement.
The honest limit. An operator with administrative rights on the host can still patch a running process or replace the file after it was measured. This does not make tampering impossible; it makes tampering attributable, and it moves the attack from editing one string in a signed message to actively subverting a running process. Everything the certification agreement rests on is built from that attributability, not from proof.
verified_build is unavailable when the participant is not on the engine's host. A simulator running on a separate machine cannot be measured, so the engine records it as unmeasured and the server MUST NOT credit it verified_build or above.
2.3 A result is only as trusted as its whole chain#
The tiers above belong to components. A result — a stroke, a hole, a round — is produced by several of them at once, and it cannot be more trustworthy than the least trustworthy thing that shaped it. So:
result attestation = the LOWEST tier among every component in its chain
The chain is every component that produced or could have changed the result:
| component | in the chain because | its level comes from |
|---|---|---|
| the simulator | it computes the result — flight, lie, holing — and declares the settings in force (round.settings) | its tier (§2) |
| each launch monitor that is the resolved source for a launch value the result was computed from | the result is only as true as its inputs | its tier (§2) |
| every other result-modifying sensor — a putting sensor, a ball-position sensor, anything whose value changed the outcome | the same reason | its tier (§2) |
| the bay itself | the room, the host machine and the engine instance every contribution passed through | the bay's standing, below |
A component that could not change the result is not in the chain. This is the same test as a critical device in 14 §2: a swing camera, a pressure mat or a wind machine that no result field resolves to does not lower anything, and a guest's A0 monitor beside the house monitor is harmless unless it wins a field the result used. Membership is decided per result from the resolution the manifest already records, not by device type.
The bay is in the chain because unsigned data passes through it. An A0 or A1 contribution carries no signature, so the host it crosses could alter it unnoticed; a signed contribution survives a tampered host, but the settings, the course and the resolution of which device won a field are the bay's. The bay's level is set by who stands behind the room, in the same shape as the component tiers:
| bay level | what it takes |
|---|---|
A0 | nothing: an installation nobody has claimed |
A1 | claimed by an account that has accepted the Bay Owner Terms or the Bay Facility & Certification Agreement, whose integrity commitment binds the owner not to modify the equipment or the software, and not to change settings undeclared |
A2, A3 | a bay certified at a tier whose definition in the bundle sets min_attestation_tier to that level (14 §1), with its certified equipment present and no shortfall. A certified bay whose tier sets a lower floor is A1 |
A certificate's floor is a statement about the installed equipment, audited by a person and signed by SGA; it is what lets the room itself vouch at the level of the devices in it. A bay with A3 equipment that nobody has certified produces A1 results, however good the hardware, for the same reason a Gold bay with immaculate equipment is not a Diamond bay (14 §1).
Every link is contractual as well as technical. That is what makes a result at a given level both verifiable and answerable for:
| link | the commitment behind A1 | the commitment behind A2 and A3 |
|---|---|---|
| simulator, launch monitor, sensors | Technology Partner Agreement: report truthfully, label provenance, declare settings honestly, build nothing that lets a player cheat, and report any flaw found that could make a product report false data | Conformance & Endorsement Agreement: the factual-data warranty, key custody, audit, remedies |
| bay | Bay Owner Terms or Bay Facility & Certification Agreement: no tampering, no undeclared changes | certification: audit, calibration, inspection, and the certificate's remedies |
Vendor assurance composes the same way. A result's vendor_assurance is the lowest among the components in its chain (none < verified_build < certified).
Who works it out. The engine records each participant's declared tier, which source resolved each field, and the bay's claim and certificate; it verifies none of it (§3a.3). The server verifies each component against its certificate, digest and agreement, and derives the result's tier at ingest. A build whose digest the server cannot verify at ingest counts below A1, and a digest verified later does not raise results already recorded. A stroke takes the tier of its own chain; a hole or a round takes the lowest of its strokes, so a monitor that joins at the 12th affects the strokes it touched, and the round as a whole, and nothing earlier. A later determination against any one component — a revoked key, a suspended digest, a lapsed certificate — lowers the effective tier of exactly the results that component was in the chain for (§3.1).
3. Device identity#
{
"device_id": "sga:device:9f2c1a4e-...",
"vendor": "acme",
"model": "AX-3",
"serial": "AX3-00119423",
"firmware": "4.2.1",
"firmware_digest": "sha256:c41d...",
"attestation_tier": "A2",
"attestation": {
"alg": "ed25519",
"nonce": "<engine session nonce>",
"sig": "0A3F21-<signature over identity ‖ capability descriptor ‖ nonce>"
}
}- Ed25519 or ECDSA P-256. Ed25519 is the default: small keys, small signatures, fast verification, nothing to get wrong, and it runs on constrained hardware. P-256 is supported because cheap secure elements do it in hardware and Ed25519 in none — excluding it would shut out exactly the devices that can protect a key best. A vendor picks one; a verifier MUST support both. RSA is not offered: the signatures are large enough to matter on a BLE link.
sigis<certnum>-<signature>. The prefix says which key to verify with, which is what makes rotation and revocation possible at all. A signature with no key id is unverifiable the moment a vendor has more than one.- The signature covers the capability descriptor, not just the identity. Signing identity alone would let a device attest to being a tour-grade launch monitor and then claim capabilities it does not have. Identity and claims are one document.
- The engine's session nonce is inside the signature, so a descriptor captured from a previous session cannot be replayed into this one.
firmware_digestis what makes "approved software version" enforceable. A version string is a self-report and easily edited by anyone with access to the host; a digest can be checked against what the vendor published.
3.1 Where the certificate comes from#
A cert number is issued through the partner portal once the vendor holds the signing endorsement (Governance) — which is what ties a key to a party who has contractually warranted that what it reports is factual.
The vendor generates the keypair and sends SGA only the public half. SGA never holds, transmits or escrows a private key, for the reason every public-key system works this way: a signature is only evidence of who made it if exactly one party could have made it. If SGA generated the key, a vendor could always answer an audit with "SGA could have signed that," and the whole chain would be worth nothing. It would also make SGA's key-issuing system a single breach that compromises every vendor at once.
Custody is declared when the certificate is created, and attested to. In the partner portal the vendor names the certificate, chooses the signature scheme, supplies the public key — and states where the private key will live:
| declared custody | tier |
|---|---|
| in firmware or NVRAM on a device | A2 |
| inside a signed executable, not trivially extractable, method attested | A2 |
| in a secure element or TPM on a device | A3 |
| on a hardware token | A3 |
They then attest, against the endorsement agreement, that the key will only be used in the manner declared.
Neither A0 nor A1 appears here, because neither signs. A0 is the absence of a relationship; A1 is the partner agreement plus published digests, with nothing to sign and therefore no key to place. A certificate is what A2 begins.
Two placements, at opposite ends#
A hardware token is A3 only at the bay. Signing happens where the software runs, so it is one token per installation; a code-signing token on a developer's desk cannot sign a round two states away. A key pasted into a binary is not A2. It is recoverable with strings, and one extraction compromises every installation of that product, which is why revocation has to be fast (§3a.4).
What revocation actually costs a vendor#
Revoking a signing certificate sounds like the end of a relationship. It is not, and the reason is that the tiers are cumulative: everything A2 and A3 require, they require on top of A1. A vendor at A3 is already publishing digests under the partner agreement, because that is what A1 is.
So when a certificate is revoked:
- The product falls to
A1, not toA0and not out of the ecosystem. - Nothing on the vendor's side has to change for that to happen. The digests they already publish are the whole of
A1, so the fallback is immediate and needs no release, no customer action and no bay reconfiguration. - Rounds keep counting toward player ratings throughout. What the product loses is sanctioned competition until it is back.
- Recovery is a new certificate and a software update, returning it to the tier it held before — and it is the natural moment to improve how the key is stored, since they are shipping a build anyway.
A certificate record is never deleted#
Verification is historical. A round played in March is checked against the certificate as it stood in March, not as it stands today — so a certificate is marked, never removed. Deleting one would make every round it ever signed unverifiable, which destroys the evidence rather than the key, and is the same mistake as discarding a journal that fails its chain.
Two timestamps, and conflating them loses something.
| means | |
|---|---|
revoked_at | when SGA acted |
compromised_from | when the key is believed to have been exposed |
The second may be backdated, because a compromise is usually discovered after the fact. Keeping both separately answers two different questions that a single field cannot: what data is tainted comes from compromised_from, and did SGA act promptly once it knew comes from the gap between them. Collapse them and backdating quietly erases SGA's response time — which is exactly the record an audit would want.
Status is a history, not a flag. The enforcement ladder (Governance) suspends before it revokes, and a suspension can be lifted. A certificate can therefore be active, suspended, restored, and later revoked, and a verifier needs to know which of those was true at the moment a record was signed. One boolean cannot say that.
A determination is scoped and recorded, never destructive. A compromised key lowers the effective tier of the records the evidence points to — a member, a bay, a facility, a device, a window of time — and nothing is deleted or edited: a new determination is recorded beside the old, and people decide the scope (Governance). Re-ingesting a journal is idempotent, so re-verification against the historical certificate state returns the same answer it did the first time.
One certificate, one custody arrangement. A vendor shipping both a PC connector and a hardware unit needs two certificates, because a single key used in both places is worth only its weakest custody wherever it appears. Allowing one certificate to span custody levels would make the higher claim meaningless the moment the key shipped in software.
A certificate's tier is immutable. Moving custody — a new hardware revision that gains a secure element — means a new certificate, never an edit. Raising a tier in place would silently change what every round already signed with that key is taken to mean, retroactively and invisibly.
The tier belongs to the certificate, not to the claim. Custody is assessed when the certificate is issued — is the key in software on a host the operator controls, in the device, or in a secure element? That answer is recorded against the cert number.
attestation_tier in the identity block is therefore a cross-check, not a trust anchor, exactly as a declared binary path is in §2.2. A device claiming a tier above what its certificate was issued at is a discrepancy, and a discrepancy is recorded rather than argued with.
Serial numbers are sensitive. They travel to SGA for certification and audit. They are not exposed to other participants in the bay, and never to the member-facing app. A spectator display has no business knowing a monitor's serial number.
3a. Signing contributions, not just the introduction#
The handshake signature proves a device joined. It proves nothing about any particular shot — and until a device signs what it sends, the engine's own signature over the journal is the only thing covering shot data, which quietly makes engine integrity the thing everything rests on.
It is the same mechanism, the same key, the same <certnum>-<signature> shape. Only the covered bytes differ:
| message | signature covers |
|---|---|
session.describe | identity ‖ capability descriptor ‖ engine nonce |
| a contribution | session_id ‖ counter ‖ shot_id ‖ canonical(payload) |
{
"type": "record.shot",
"field": "ball.speed_mps",
"shot_id": "sga:shot:01J9F3B0W8K2M6Q4T7XZ",
"ball": { "speed_mps": 53.6, "spin_rpm": 2740 },
"attest": { "counter": 47, "sig": "0A3F21-<signature>" }
}Each element blocks something specific:
session_id— a signed contribution cannot be carried into another session.counter— monotonic per device per session, so a contribution cannot be replayed or reordered within one. It MUST NOT reset on reconnect; binding it to the engine's session nonce achieves that without persistent storage.shot_id— blocks re-attribution, which is the one a reader usually misses: without it, a genuine signed "the ball went 53.6" can be filed against a different stroke.record.swing_detected(06 §10) hands the device itsshot_idbefore it reports, so this costs nothing.- canonical(payload) — canonicalized per 00, the same way the journal chain already is, so a second implementation computes the same bytes.
Sign what you assert, never the composed record. A device never sees the composed record and could not sign it: the engine authors slot, manifest, bundle_version and t_ms. A signature covering those would break the instant the engine did its job. The rule is canonical(the message, with sig removed) — one boundary rather than a field list, because a list invites a vendor to leave out the field most worth tampering with and still pass.
3a.1 Signing MUST NOT sit on the critical path#
The latency budget is 10 ms end to end (09 §7). Signing does not fit inside it on the hardware that matters: an external secure element takes tens of milliseconds, and it is precisely the A2 and A3 devices — the ones whose signatures carry the most weight — whose keys live in one.
So a device SHOULD send its contribution immediately and let the signature follow, carrying the same shot_id. The simulator needs ball speed to draw a shot; it does not need a signature to draw anything. The signature is for the record, which is read minutes to months later.
A device wanting immediate tamper-evidence as well MAY additionally carry a symmetric MAC — HMAC-SHA256, truncated to 128 bits — keyed from the handshake. That is a different job: it detects tampering in flight between device and engine, is verifiable only by the engine, and is never what the cloud relies on. A MAC cannot serve as attestation, because whoever can verify it can also forge it.
3a.2 Optional, and its absence is recorded rather than assumed#
A device that cannot hold a key is not refused. It joins at the tier its custody supports, and A0 is a first-class outcome rather than a failure.
Nothing new is needed to record that: every shot carries the manifest hash, and the manifest names each field's source and that source's tier. So "this ball speed came from an unattested device" is resolvable months later. Unsigned does not become invisible; it becomes labeled, and a ruleset decides what the label is worth.
3a.3 The engine records, the cloud verifies#
The engine does not verify participants' signatures, and holds no private key. The only signatures it checks are SGA's, on a bundle and on an Event Rules Package, against the root key built into it (04, 08 §1.1). It cannot usefully verify participants: an engine whose integrity is in question cannot vouch for anything, and a verification result it reported would have to be trusted to be worth having. It records verbatim and forwards.
This is simpler than the alternative in every direction — no trust store at the bay, no certificate distribution, no revocation lag, nothing to go stale — and it makes an offline bay behave identically to an online one, because nothing at the bay was load-bearing to begin with.
The server layer verifies: it resolves the cert number, checks the signature, reads the tier the certificate was issued at, and decides what the data is good for. Verification and use are one decision, made once, by the party that can be held to it.
Two consequences follow, and both are addressed elsewhere rather than at the bay. A vendor gets no local feedback that its signing is wrong — which is the conformance kit's job, before they ship. And whether a declared tier was true is determined afterward, at ingest. At the bay, the engine still compares declared tiers and the bay certificate with an event's floors and refuses to start a sanctioned round that fails them (14): the bay check protects the player, and ingest is the authority.
3a.4 Live verification, because the bay is almost always online#
Treating the offline bay as the normal case would be pessimism. Almost every bay has a path to the internet, directly or through a logged-in phone, and ingestion is continuous rather than a nightly batch.
So an engine MAY ask the server to confirm, at device connection and periodically thereafter, that the devices in this bay are producing signatures that verify against the certificates they claim. The answer is advisory at the bay — it changes nothing about what is recorded — but it means a problem is known in minutes rather than discovered after an event.
That is what makes the trust layer usable for tournament start timing: an official can be told before play that a bay's equipment is signing correctly at the tier the event requires, without the bay itself ever being the authority on that question.
And a failure has somewhere to go. A bay resolves to a facility_id (14), and SGA holds the operator's contact details, so a device failing its claimed tier reaches the person who can unplug it. When one model or vendor starts failing across many sites at once, that is a different signal with a different audience — it goes to the vendor's technical contact, and SGA checks whether any scheduled event depends on that equipment.
4. Bay binding#
Device identity says what. Bay binding says where, and it is the link that actually resists the interesting attack: a certified device, genuinely signing genuine data, sitting somewhere other than where the round is claimed to be played.
- Each certified bay has a placard carrying
sga:bay:0482-07and a signed, static pairing token. Printed once, and it does not change. - A device joins by presenting the token. Physical presence at the bay is the pairing authority.
- Binding is recorded in the session manifest and signed by the engine.
- A device that cannot present a bay token joins unbound, and every shot it produces is marked unbound. Unbound is not refused — it is recorded, and the competition rules decide whether an unbound round can be sanctioned.
4.1 The placard says where. It cannot say when.#
A printed sign cannot rotate, so place and liveness are separate mechanisms:
| mechanism | proves | can be photographed and reused |
|---|---|---|
| printed placard token | which bay this is | yes — trivially |
| rotating proximity token (07 §2.2) | someone was here during this window | no |
So the placard establishes identity of place, and that is all it is asked to do. It names the bay, it survives having no power and no network, and it still works when a member is standing in a barn in Nebraska with one bar of signal. Liveness comes from somewhere else.
A bay MAY additionally display a rotating code on a screen it already has — an e-ink tag, the bay monitor, a mounted tablet, or the operator's own application. Where one exists it is a stronger bay token than the placard and SHOULD be preferred. It is not required, because requiring a powered display at every bay would exclude exactly the residential and low-cost installations this standard is meant to reach.
4.2 Which way the proximity token travels#
Rotation is carried over BLE, and the direction matters more than it looks.
Normative: the member's phone advertises, and the engine scans. The token is derived from a secret shared between the member's app and SGA (07 §2.2), the engine records what it observed and when, and SGA verifies afterward that those tokens are the ones that phone should have been broadcasting in that window.
The obvious inversion — a beacon at the bay, detected by the phone — is weaker, and it is worth saying why rather than leaving it to be rediscovered. It makes the phone the reporter, and the phone belongs to the party with the motive. A confederate standing in the bay can capture the bay's beacon and forward it to a player at home, who then reports having seen it. The relay is cheap, needs no special equipment, and leaves no trace in the data. Having the engine — fixed, certified, and not owned by the player — do the observing removes that whole class of attack.
A bay-side beacon MAY be operated in addition, as a convenience for waking the app and confirming to the player which bay they are at. It MUST NOT be the sole evidence of presence.
Residential bays get identity and binding on the same mechanism but are never publicly listed, by design. The certification number exists; the location does not appear in facility search.
4a. Pairing a device that travels#
A rack belongs to a bay. A tagged golf bag belongs to a person and moves between facilities. Both join the same way, and neither tells the bay whose it is.
4a.1 Ownership does not belong at the bay#
Ownership does not survive contact with how people actually play. Friends and family share clubs constantly. A bag attributed to its owner would mis-attribute every shot someone else took from it, and the mis-attribution would look like data rather than like an error.
And nothing needs it. A club specification is a fact about a club, not about who owns it -- when a son swings his father's 7-iron, the loft and shaft are unchanged and the record is correct without knowing whose bag it came from. Registration is a platform relationship: the member registers the device in the app, and the cloud resolves device_id to an owner from its own registry whenever it needs to. The bay never has to know, so it should not be told.
So a traveling device presents a bay token and nothing else, exactly like a fixture. It is another holder.
4a.1a Shared equipment needs no special case#
A couple traveling with one set of clubs is the test of whether §4a.1 is right, and it passes without a mechanism.
- Either plays alone. The bag asserts which club is out; the engine attributes it to the active slot. Correct for whoever is swinging.
- Both play together. Same thing. Slot 1 is up, slot 1 gets the club; slot 2 is up, slot 2 does.
Registration is a platform relationship and should be many-to-many: a device may be registered to several members, one as owner and the rest with rights to manage it. Households share equipment; refusing to model that buys nothing and creates a support case the first week.
Two honest consequences.
A shared bag produces more ambiguity than two bags. Two people playing ready golf will both have a club out at once, and per §4a.3 the field composes to nothing while that is true. That is not a fault -- nobody does know which club is being swung -- but it means a shared bag yields less per-club data than two separate ones, and players should not be surprised by it.
The candidates are not lost: the ambiguity entry in the journal records every value and every source, so a later review can see the club was one of two even though no single answer was composed.
Registration must not become tracking. A bag registered to two people is seen at bays. Reporting those sightings to co-registrants would turn shared household equipment into a location trail, which is a genuinely bad thing to build by accident from a feature that sounds helpful. Default to telling nobody; if "where are my clubs" is ever offered, it is opt-in, per registrant, and obvious.
4a.2 A screenless device cannot scan a placard. A phone can.#
The bay token comes from the physical bay, which works for anything with a camera or an operator. A golf bag has neither.
So a bay token may be relayed: the member's phone scans the placard and hands the token to the device over BLE. The phone is not vouching for the device -- it is carrying a token the device then presents itself, validated exactly as a directly scanned one would be.
placard --scan--> phone --BLE--> bag --session.describe(bay_token)--> engine
Worth having beyond bags: a rack, a mat or a wearable has the same problem and the same answer, and it costs nothing because the phone is already at the bay scanning that placard to check the player in.
4a.3 Ambiguity is a property of the bay, not of a device#
Several holders can be present at once, each authoritative about its own inventory. The question is never whose holder a club came from -- it is whether the bay as a whole knows which club is in play.
One club out across the bay is unambiguous, however many holders there are. Two clubs out is ambiguous, even from one holder.
So the engine composes standing state across every source:
- All live sources agree -- that is the value, and corroboration from more than one is recorded.
- They disagree -- the field is absent, and the disagreement is journaled. Nobody knows which club is being swung, and the honest answer is nothing.
- One retracts -- if a single assertion remains, it resolves cleanly again.
That last case is the ordinary rhythm of a round: someone pulls a club, hits, puts it back. The ambiguous window is the few seconds when two clubs are out at once, and it closes on its own.
This is a general rule, not a club-identity one. Any standing field several sources disagree about composes to nothing, for the same reason: a confident wrong answer is worse than a missing one, and choosing whichever device spoke most recently is the worst possible tie-break.
5. What the engine signs#
The engine is the notary for the session. It signs, over the session key:
- the session manifest and every subsequent version — the complete technical configuration and its full change history
- the journal, as a hash chain: each record includes the digest of its predecessor, so a record cannot be removed or reordered after the fact without breaking the chain
- round boundaries, and the bundle and ruleset versions in force
A hash chain is not a blockchain and does not need to be. It gives tamper-evidence, which is what an audit trail requires; it does not give tamper-proofing, which would require a consensus mechanism nobody in this system wants to operate. An operator with local access can still delete the whole journal — they simply cannot alter part of it and have it verify.
The engine holds no PII. It signs slots, not people. See 00-conventions §6.
5.1 What belongs in the journal, and what does not#
A journal is a record of what happened. Standing state is not a sequence of happenings: it is a level that holds until it changes. A simulator re-asserting environment.wind_speed_mps = 3.1 every four seconds for an hour has not reported nine hundred events, it has reported one fact and then kept saying it.
That repetition is required on the wire and is required there for a reason: max_age_ms makes a standing value expire, so a device that stops speaking stops being believed, and a reconnecting participant re-asserts what it holds (09 §5.2). None of that says the journal must keep every copy.
Kept, those copies can be half a journal, with the shot records the round actually rests on a tenth of it.
Rules:
- The journal MUST contain the first assertion of a standing field, every change to its value, and every withdrawal. Those are the events.
- It MAY omit a re-assertion that changes nothing.
- An engine MUST make that decision before appending. Records MUST NOT be removed, rewritten or compacted after the fact: each record carries the digest of its predecessor, so a journal that is thinned afterward is a journal that no longer verifies — which is indistinguishable from one that was tampered with, and is meant to be.
- Rotating secrets MUST NOT be journaled. A check-in or claim code is a short-lived proof that somebody stood in front of a screen (03 §3.4c); writing one into a permanent, shippable record outlives the window it was minted for and undoes the point of rotating it.
What a repetition proves, and what replaces it#
Dropping the repeats costs something real. A re-assertion is evidence that the device was still there and still standing behind the value. Without it, silence is ambiguous: the value was unchanged, or the device died and the engine coasted on what it last heard. The engine cannot coast — max_age_ms stops it — but an auditor reading the journal a month later cannot see that it did not.
So the evidence is kept, in a form that does not grow with time:
| record | is |
|---|---|
standing_digest | the digest of the whole standing set, as it stood at that moment |
{ "kind": "standing_digest",
"fields": ["ball.speed_mps", "environment.wind_speed_mps", "origin.lie_surface"],
"digest": "sha256:9f2c…",
"expires_at_ms": 600000 }digestis over the RFC 8785 canonical form of the resolved standing set — field, value and source for every field in force — so it is reproducible by anything that has the values, and proves nothing on its own about what they were.- An engine SHOULD append one at a fixed interval while a round is running, and MUST append one immediately before composing a shot record.
That second rule is what makes it evidence rather than decoration. A shot record already carries the standing values it was composed from; the digest written beside it says those values were the live set at that instant, asserted by the devices named, and not a stale reading the engine had been holding since the last hole. A few dozen bytes proves cryptographically what several kilobytes of repetition only suggested.
What this does not touch#
- Reconciliation (06 §9.1) is the scorecard against the shot stream. Neither is standing state.
manifest_hashcovers the manifest, which is not affected.- A consumer rebuilding state from the journal reads last-known-value, and the first assertion of every field is still there — a change from nothing is a change.
6. Integrity status, and what it is for#
Four statuses:
| status | meaning |
|---|---|
verified | Every link in the chain proven at the required tier. |
verified_with_exception | A link failed, was reviewed, and was accepted. The exception is recorded. |
pending_review | Evidence requires human judgment. |
unverified | Does not qualify for official ranking. |
Two rules that should be hard to change later:
The system flags. Humans adjudicate. No automated process declares misconduct. The chain produces evidence; a person reaches a conclusion. It is the only defensible posture when prize money is involved.
unverified is a status, not a rejection. The round is still recorded in full, still visible to the player, still counts for their own statistics. It does not feed sanctioned ranking. A platform that discards data it cannot certify loses exactly the data it would need to work out why it could not certify it — and tells the player their round never happened, which is the fastest way to lose the player.
8. Every hash and signature, and what each is for#
Two different jobs, and conflating them is how a system ends up believing it has more assurance than it does.
A hash gives integrity: this content has not changed. A signature gives authenticity: this content came from a party you trust.
The standard uses both.
8.1 The hashes#
All SHA-256, all rendered sha256:<64 hex chars>.
| field | covers | answers |
|---|---|---|
descriptor_hash | a participant's signed capability descriptor | what did this device claim it could do, and at what provenance |
manifest_hash | the manifest, with its own hash field blanked | what was the bay's entire configuration at this moment |
previous_manifest_hash | chains manifests within a session | was a configuration change removed from the history |
journal hash / prev_hash | each record plus its predecessor | was any record altered, removed or reordered |
firmware_digest | the firmware build the device is running | is this the build the vendor published, or a version string somebody typed |
course.digest | the course file | are two bays playing the same golf course |
previous_checkpoint_hash | chains scorecard checkpoints | was a hole's result quietly dropped |
seriesRef.digest | referenced time-series data on the bulk path | did the swing trace change after the fact |
8.2 How they resolve, from one shot#
The point of the arrangement is that a single hash on a shot record leads to everything else:
shot record
manifest: sha256:7a3f...
|
v
session manifest 7a3f
participants[].descriptor_hash: sha256:c41d...
previous_manifest_hash: sha256:9b02... --> the whole config history
|
v
capability descriptor c41d
provenance, resolution, conditions, id_methods
identity.firmware_digest: sha256:e5aa...
|
v
the vendor's published buildSo "was this spin figure measured or modeled, by what hardware, on what firmware, under what configuration" is one lookup from the shot, answerable years later. That is the whole reason provenance is declared once and referenced rather than repeated per shot.
course.digest sits outside that chain and answers a different question, which is the one tournaments turn on: name and version are labels, the digest is the course.
8.3 The signatures#
| signature | signer | prevents |
|---|---|---|
identity.attestation | the device | a descriptor being forged or replayed from another session |
manifest signature | the engine | a configuration history being rewritten wholesale |
event rules package signature | SGA, verified by the engine against a pinned root | an event's settings being altered, replayed or kept past their time on the way to a bay |
| bundle signature | SGA, verified against a pinned root | a modified field catalog being loaded |
bay profile signature | the engine | a certification submission being edited |
The device attestation covers identity, the capability descriptor and the engine's session nonce together. All three matter: signing identity alone would let a device attest to being a tour-grade launch monitor and then claim capabilities it does not have, and omitting the nonce would let a descriptor captured from a previous session be replayed into this one.
8.4 What hashes alone do not give#
Hashes alone give tamper-evidence, not tamper-proofing, and not authenticity.
A hash chain proves nothing was altered piecemeal. It does not prove the records came from a device you trust, and it does not stop someone who controls the machine from discarding the journal and writing a new one with consistent hashes throughout. Signatures are what close that.
What integrity buys on its own is real and useful: an operator cannot quietly change one score, a checkpoint cannot be dropped from a round, and a shot cannot be edited after the fact without the chain breaking. What it does not buy is protection from someone willing to destroy and replace the whole record -- and detecting that is the platform's job, through upload continuity and the trust chain, not the journal's.
8.5 Canonicalization#
Documents are canonicalized per 00 §3b (RFC 8785) before hashing or signing. Self-referential fields — manifest_hash inside a manifest, hash inside a journal record — are omitted before canonicalizing, not set to empty.
9. Open questions for vendor review#
Flagged rather than decided, because deciding these without the manufacturers in the room would be guessing:
- Revocation for a bay that never comes online. Bundles can carry revocation lists, and §3a.4 covers the online case, but a bay offline for a year is a real gap with no clean answer.
- Secure element availability. Which shipping monitors have one at all? This determines whether
A3is reachable this decade or is aspirational. - Replay windows. Nonce-based freshness needs a tolerance. Too tight breaks flaky BLE links; too loose admits replay.