SGA‑OISOpen Interoperability Standard
1.0 Draft
SGA-OIS/Specification

Offline operation, store-and-forward, and presence

Normative where marked.

Two capabilities that look unrelated and share one mechanism: a member's phone, present at the bay, participating in the session as an ordinary SGA-OIS device.


1. The phone as a courier#

A bay with no internet still plays, still records, still signs. What it cannot do is upload — and what it cannot receive is a newer bundle.

If a logged-in SGA app is in the room, it can carry both.

   offline bay                phone (has cellular)              SGA cloud
        │                            │                              │
        │──── BLE advertise ────────►│                              │
        │◄─── consented pairing ─────│                              │
        │                            │                              │
        │──── signed journal ───────►│───── upload verbatim ───────►│
        │                            │                              │
        │◄─── signed bundle ─────────│◄──── cached opportunistically│

This works because of the signing already specified, not in spite of it.

  • The journal is hash-chained and signed by the engine (02 §5). The phone cannot alter a record, remove one, or reorder the chain without the break being detectable at the cloud.
  • Bundles are signed against the pinned SGA root (04 §3). The phone cannot forge one, and it cannot serve a stale one, because bundle downgrades are refused.

So the phone is an opaque courier. It carries sealed envelopes it cannot open in any meaningful sense and cannot alter without detection. That is the whole security argument, and it is why this mechanism costs nothing extra to add — the properties that make it safe were already required for other reasons.

1.1 Transport tiering#

The courier is the bottom rung of the uplink ladder in 09 §6.2 — where a bay goes when it has no route to the cloud at all. This section is about what it can carry once it is there.

BLE throughput in practice is single-digit to low-tens of kilobytes per second even on BLE 5. That is fine for some payloads and hopeless for others, so the standard tiers them:

tierpayloadtypical sizetransport
1scorecard, shot records, manifests, audit chain20–60 KB compressed per roundBLE acceptable
2bundle0.5–5 MBprefer Wi-Fi; BLE only as last resort
3diagnostic frames, swing videotens of MB+never over BLE; wait for real connectivity

A complete round's competitive record is tier 1. Eighty shots of structured JSON compresses to well under a minute of BLE. The thing that must not be lost is small.

Where the phone and the bay can both reach the same LAN, or the phone can offer a hotspot, bulk transfer moves there and BLE is used only for discovery and control. Implementations SHOULD attempt the faster path first.

1.2 Rules#

  • Courier participation MUST be the member's explicit choice, presented separately from membership and from playing, because it uses their battery and their data plan. The choice MAY be pre-selected where the law allows a pre-selected choice, and MUST NOT be where the law requires consent. The member MUST be able to change it at any time.
  • The member MUST be able to restrict it to Wi-Fi, or to tier 1 only.
  • The engine MUST NOT hand a courier anything it would not upload directly. Consent and masking rules apply at the point the journal is sealed, not at the point it is handed over.
  • A courier MUST NOT receive PII for other players in the session. It is carrying slot-bound records; per 00 §6 there is no PII in them to begin with.
  • Multiple couriers MAY carry the same journal. The cloud deduplicates by chain digest. Redundancy is free and the failure mode — one member leaving early — is common.
  • A journal that fails chain verification at the cloud is quarantined and reported, never discarded. A broken chain is evidence.

An interesting second-order effect: a facility that never connects a bay to the internet still ends up with current bundles and uploaded rounds, purely as a side effect of members playing there. The network heals itself through attendance.


2. Presence#

The question is whether a member was physically at the bay while their round was being recorded. It matters only for sanctioned competition, and it should never be required for ordinary play.

2.1 Why the MAC address approach cannot work#

Reading the phone's own MAC and reporting it to the cloud is a reasonable idea — it is consented self-attestation, not harvesting, and it does not have the legal problems of sniffing. It is blocked for a different reason:

Neither platform lets an app read its own hardware address any more. iOS has returned a constant (02:00:00:00:00:00) since iOS 7. Android returns a constant since Android 6. Both platforms additionally randomize the address they present per network and rotate it on a schedule; BLE addresses are resolvable private addresses that also rotate, and an app cannot read the current one it is broadcasting.

So there is no MAC for the app to report, and the engine cannot observe a stable one either. The idea is sound and the API surface no longer exists.

2.2 What works instead, and works better#

A rotating proximity token. The member's app and the SGA cloud share a session secret. The app broadcasts a BLE advertisement carrying a token derived from it:

token = truncate(HMAC(session_secret, floor(t / 300s)))

The engine scans, records the tokens it observed and when, and uploads them with the round. The cloud recomputes what that member's app should have been broadcasting during that window and checks.

This is the same construction as the Apple/Google exposure-notification scheme, which has been analyzed hard by people who do this for a living, and it is strictly better than a MAC on every axis that matters here:

  • Cryptographic, not an identifier. A captured token proves presence in a time window and cannot be replayed into another one.
  • Unlinkable. Anyone without the secret sees rotating noise. It is not a tracking beacon for bystanders and it collects nothing from devices not enrolled in the session.
  • Unprivileged on both sides. BLE scanning and advertising are ordinary app capabilities. Nothing here needs elevated privilege (05).
  • Works on iOS, which MAC-based schemes do not.

The advertisement carries the fixed presence UUID allocated in 03 §5.1, 738B0010-2551-4C83-BACE-C49B0FCE933A. That UUID MUST NOT rotate even though the token does: a fixed one lets an engine push a filter into its radio, and a rotating one would force every engine in a busy facility to receive, process and journal every advertisement in the building, because it could not tell which were beacons. 03 §5.1 has the reasoning.

Continuity, not distance#

Every advertisement the engine receives arrives with RSSI — the strength the radio measured it at. It costs nothing extra: no additional packet, no connection, no configuration. The bay is already listening to these advertisements to record the token, and the loudness is stapled to each one whether or not anybody writes it down.

The question this answers is not "how far away." It is whether the member was there the whole time. Consider the case the standard has to survive: the winner of a championship was at the bay for the first two holes, absent for most of the round, and back for the last. Every stroke is recorded, every signature verifies, and the scorecard is impeccable. The thing that is wrong with that round is a shape in time, and it is invisible to everything else the standard collects.

So the engine records a timeline, and the timeline is the deliverable. Absolute levels are of minor interest; appearance, disappearance and return are the signal.

RSSI must not become a distance. 2.4 GHz is absorbed by the human body. The same phone at the same spot reads 15–25 dB weaker in a back pocket facing away than in a hand held up — so a member turning around moves the number more than walking three meters does. Converted to meters it would be a confident fiction, and confident fictions are what disqualify the wrong person.

  • The engine MUST record the RSSI it measured and MUST NOT record a distance derived from it.
  • The engine SHOULD NOT derive a proximity band either. Banding is an interpretation, thresholds are policy, and policy that is compiled into a bay cannot be revised when it turns out to be wrong. Where a band is recorded anyway it MUST be carried alongside the RSSI and never in place of it.
  • Per 01 these are different kinds of claim: the RSSI figure is measured — a radio genuinely measured it — and any band derived from it is inferred.

This is 00 §7 in its most literal form: the engine measures, and the cloud, the ruleset (08) and the officiator decide what the measurement is worth.

Observed absence is not the same as absent observation#

This is the rule that makes the timeline usable, and omitting it would make the feature dangerous rather than merely incomplete.

Fourteen holes with no proximity records can mean the member left. It can also mean the engine's Bluetooth adapter wedged, the scan thread died, the app was killed by the platform, or a microwave oven ran. In the journal those are identical, and the difference between them is the difference between a disqualification and a bug.

Silence cannot carry this. The engine must say it was listening.

recordis
presence_scanthe engine was scanning across this window, and these enrolled tokens were heard — possibly none
  • An engine MUST append a presence_scan record at a fixed interval throughout a round, including when it heard nothing. A window with an empty result is the evidence; a window with no record at all is an admission that nothing is known.
  • A presence_scan MUST report the scanning radio's own health. An adapter that is absent, disabled or erroring MUST be reported as such rather than as an empty result, because "I listened and heard nobody" and "I could not listen" are the two readings this whole section exists to separate.
  • A consumer MUST NOT treat a gap in presence_scan records as absence of the member. It is absence of evidence, and it is the engine's fault.

This is the same construction as standing_digest in 02 §5.1, for the same reason: once a journal stops repeating itself, something has to prove the silence was observed rather than suffered.

Sampling#

A beacon heard once a second for a four-hour round is fourteen thousand near-identical observations, and journaling all of them repeats exactly the mistake 02 §5.1 exists to stop.

  • The engine SHOULD journal an observation on a material change — first acquisition, loss, reacquisition — rather than per advertisement.
  • The engine MUST journal one immediately before composing a shot record for that member's stroke.

That second rule is the one that earns its place. It puts the member's own proximity beside their own stroke, at the instant it was struck, hash-chained to everything around it — which is precisely the question an adjudicator is asking, and it costs a few dozen bytes a shot.

What the app is doing is a second signal, and a separate one#

An app can advertise differently when it is in the foreground than when it is backgrounded. That is free — the packet is already being sent — and it is genuinely informative: a member with the app open and on screen is engaged with the round in a way a phone in a pocket is not.

It is recorded as its own observation, and the reason is a platform hazard rather than a philosophical one.

On iOS, background BLE advertising is restricted — the service UUID is moved to an overflow area that a non-Apple scanner may not reliably see. The bay engine is not an Apple device. So a phone that goes into a pocket can become unhearable while sitting two meters from the mat, and if app state and presence were the same signal, pocketing a phone would be indistinguishable from walking out of the building. That is the exact false accusation this section is built to avoid.

  • The app SHOULD distinguish foreground from background in its advertisement.
  • An engine MUST record app state as an observation distinct from presence, and MUST NOT infer departure from a change in it, or from the disappearance of advertisements that a platform may simply not be delivering.
  • Implementations MUST measure this on the platforms they support rather than assume it. Whether a given engine radio can hear a given phone's background advertisement is an empirical property of two pieces of hardware, and a presence system built on an untested assumption about it is a presence system that accuses people.

None of this adds PII. The token is unlinkable to anyone without the session secret (§2.2) and a signal strength is not an identifier, so the journal stays as it was under 00 §6.

A held connection measures below the scheduler#

The weakness of everything above is that it depends on the platform choosing to run an app. Advertising is throttled when an app backgrounds, scanning is coalesced, and on iOS the advertisement may become unhearable to a non-Apple scanner entirely. Those are scheduling decisions, and an app cannot argue with them.

A maintained connection sidesteps the argument rather than winning it. Once a link is established, the two controllers exchange packets every connection interval to keep it alive — that is the radio, not the application — and the central can read the signal strength of those exchanges. The measurement moves below the layer that was throttling it.

That yields a presence signal with properties the beacon cannot have:

beaconheld connection
samplingwhen the platform permits an advertisementevery connection interval, 7.5 ms – 4 s
survives backgroundingplatform-dependent, and poorly on iOSthe link is maintained by the system
loss of signal meanspossibly departure, possibly schedulinga disconnection, which is an event, timestamped

The third row is the one that matters for §2.3. A beacon that stops is ambiguous. A link that drops produces a disconnection at a known instant, and a reconnection at another — which is exactly the timeline a round contested on attendance turns on.

Rules

  • An engine MAY hold a connection to a member's phone for presence, where the member has consented to it.
  • Where a connection already exists for another reason — relaying or introducing the member's equipment (13 §4.1), or carrying a journal (09 §6.6) — presence SHOULD ride it rather than open a second. Any one of the three purposes justifies the link; the other two are then free.
  • Ordinary play MUST NOT require a held connection. A competition ruleset (08) MAY require one for a sanctioned event, which is the only place the cost is worth paying.
  • Presence MUST NOT take a controller connection slot that a measuring device needs. BLE controllers support a small and finite number of simultaneous links — commonly four to eight — and a bay with a launch monitor, a bag sensor and four phones can exhaust one. The launch monitor comes first, always.
  • An implementation MUST degrade to the beacon when a link cannot be held, and MUST NOT treat the absence of a held connection as absence of the member.

A useful side effect. In an event where the link is held from check-in anyway, the courier path is already established when the uplink fails: no arming delay, no discovery, no consent prompt at the worst possible moment. The expensive part of 09 §6.6 was always establishing the channel, and a sanctioned event has already paid for it.

And a standing caution. Every claim in this subsection about how a platform treats a backgrounded application is an empirical property of that platform on that day, not a guarantee, and platforms revise these behaviors without consulting us. Implementations MUST measure rather than assume, and the graded structure of §2.3 exists so that a signal turning out to be unusable costs the standard a row in a table rather than a redesign.

2.3 Layering#

Presence is graded, not binary. Strength increases down the list, and the competition ruleset — not this standard — decides what a given event requires.

signalprovesstrength
QR bay placard at check-in (03 §3.4a)a phone was at the bay at one instantbaseline
rotating proximity token during playa phone stayed at the bay throughoutgood
proximity at the member's own strokes (§2.2)a phone was at the mat when their shots were struck, not across the roombetter
held connection through the round (§2.2)a phone stayed in range continuously, with departures and returns timestamped as eventsbetter
shot-cadence correlation with the appthe phone was active while shots were struckgood, free
biometric unlock at check-inthe member's finger or face unlocked their phonebetter
camera verificationa person was at the baystrongest

Only the last distinguishes a member from their phone in someone else's pocket. Everything above it is a phone-presence claim, and the standard should say so rather than let a championship rest on a misread signal.

These signals triage. They do not decide.#

No row in that table is capable of establishing that somebody cheated, and the standard should not pretend otherwise. A weak signal has too many innocent explanations — a flat battery, a pocket, a platform that stopped scheduling an app, a microwave oven — and any rule that converted one into a verdict would be wrong often enough to be worse than having no signal at all.

What they do is route attention. A round whose presence evidence is thin is a round worth a person looking at, and the value of that is entirely in directing scarce review effort at the cases that warrant it.

Which means the threshold is a function of stakes, not of the signal:

  • A practice round on a Tuesday with thin presence evidence is a practice round on a Tuesday.
  • The same evidence under the winner of a significant prize is worth an hour of somebody's time.

The evidence is identical in both; what differs is the consequence of being wrong, and that is a judgment the ruleset and the officiator make with the whole context in front of them.

  • A consumer MUST NOT treat any presence signal, or any combination of them, as dispositive (00 §7).
  • A ruleset MAY set review thresholds that scale with the consequence of a result. This standard specifies what was observed; it does not specify when that is interesting.

2.4 Rules#

  • Presence signals MUST be opt-in and scoped to a session.
  • The engine MUST NOT record proximity tokens from devices not enrolled in the current session.
  • Tokens MUST rotate at no more than 300-second intervals and MUST NOT be derivable to a member identity by any party except SGA.
  • Absence of a presence signal MUST NOT invalidate a round automatically. It lowers the round's integrity status and a human adjudicates — same rule as everywhere else in this standard.
  • The engine MUST record what it observed and MUST NOT score, threshold, or flag on it (00 §7). Presence is the case that motivated that rule and is fully governed by it.
Simulated Golf Association · SGA-OIS · generated from the specification sources