The live hq.raydata.co Access policy is literal-email — close the item, but the login method is one-time PIN, not the Google SSO the vault assumed
The question
Is the live hq.raydata.co Access policy literal-email or domain? — quick check, then either close or fix.
Follow-up #2 from the 2026-05-11 Cloudflare Tunnel security brief, and item #1 on that brief's "live discipline TODAY" checklist — an open founder action since May.
What we already know (from the vault)
- [[2026-05-11-cloudflare-tunnel-security-mac-mini.md]] named the Access policy as the load-bearing control for the Mac-mini tunnel surface, and named domain-wide email match (
@raydata.co) instead of literalben@raydata.coas failure mode #5: "If a future RDCO hire signs up for a Google Workspace account, they suddenly have access to the founder's HQ. Use literal email for v1." - The same brief's gap table logged this row: "Access policy literal-email vs domain | Proposal says
ben@raydata.coonly | Best practice confirmed | Verify the live policy matches the proposal — one-time check" — i.e. the vault has only ever had the proposal as evidence, never the live config. - [[2026-04-22-hq-proposal-v1.md]] is that proposal, and it specifies the login method as Google SSO in four places: "Requires Google SSO with
ben@raydata.co", "Cloudflare Access (Google SSO @raydata.co only)", and a Day-1 task "Access policy (Google SSO, ben@raydata.co only)". - [[2026-07-16-fat-cat-board-router-token-scope.md]] closed the sibling follow-up (#3) by checking the live API and finding the
board-routerservice token never existed — establishing the pattern that these May checklist items were written against a proposal, not against production, and must be verified against live config. - [[2026-05-01-fat-cat-council-architecture-proposal.md]] asserted "Cloudflare Access. Already in production for hq.raydata.co" — the claim this brief now grounds in raw evidence.
What the live config says
VERIFIED live, read-only, 2026-07-22 via ~/.claude/scripts/cloudflare-api.sh against account 5d4a0efe3ae671cf9cf3265eb1ff738c ("Ben@raydata.co's Account").
Six Access applications exist. The target app:
{
"id": "c0c24227-b228-45bf-81df-45943b56ccd4",
"name": "HQ Ray Data",
"domain": "hq.raydata.co",
"type": "self_hosted",
"session_duration": "24h",
"allowed_idps": [],
"auto_redirect_to_identity": false,
"policies": [
{
"id": "d9a4fa4e-d3d9-4145-93e3-beabe8bd2606",
"name": "Allow Ben only",
"decision": "allow",
"include": [ { "email": { "email": "ben@raydata.co" } } ],
"exclude": [],
"require": [],
"session_duration": null
}
]
}
This is an email rule (literal address), not email_domain, not an access group, not a login-method rule. One policy, one include, decision allow. No exclude, no require, no bypass policy, no non_identity (service-token) policy on this app. Session duration 24h. Not even ray@raydata.co is on it.
Account-wide there are zero Access groups (GET /access/groups → "result": []), so nothing can be widening the policy indirectly.
The sibling API app, for completeness:
{
"id": "44f3e75d-298f-4cf8-bad0-08078ac78b2b",
"name": "HQ Ray Data API",
"domain": "hq-api.raydata.co",
"session_duration": "24h",
"policies": [
{ "name": "Allow Ben", "decision": "allow",
"include": [ { "email": { "email": "ben@raydata.co" } } ] },
{ "name": "Allow hq-frontend service token", "decision": "non_identity",
"include": [ { "service_token": { "token_id": "6b0fa8f4-dd8a-41c6-ab4f-87acf7cef600" } } ] }
]
}
The service token is hq-frontend-to-api (client_id d409f137c2c6036fa5274abffbff7c88.access), scoped to the hq-api.raydata.co app only — it does not appear on the hq.raydata.co app. That is the correct per-app scoping the May brief asked for. Its expires_at is 2126-03-30T11:02:00Z — a 100-year expiry.
The quick-sites apps alongside (checked as instructed, not the target) are all literal-email as well: *.rdco.dev "owners only" = ben@raydata.co + ray@raydata.co; home.rdco.dev and demo.rdco.dev add bwilson668@gmail.com; ink.rdco.dev = akim@phdata.io + ben@ + ray@. No email_domain include rule exists anywhere on the account.
The one live/vault divergence: the only identity provider configured on the account is
[ { "id": "cb1a29d5-a57c-4f20-a877-db9f8ead64ec", "name": "", "type": "onetimepin" } ]
There is no Google/Workspace IdP. allowed_idps: [] on the app means "all configured IdPs," and the only one configured is one-time PIN. So HQ login is email OTP to ben@raydata.co, not the Google SSO the proposal and the May brief both describe.
Commands run (all read-only GETs; nothing mutated):
cloudflare-api.sh whoami; GET /accounts/<acct>/access/apps?per_page=50; GET /accounts/<acct>/access/apps/{c0c24227…, 44f3e75d…, ddd7fa5b…, 31f6d6eb…, 88aac54a…, 49e1de4a…}; GET /accounts/<acct>/access/groups; GET /accounts/<acct>/access/identity_providers; GET /accounts/<acct>/access/service_tokens.
What the web says
- Cloudflare's own common Access policies treat an allow rule for a specific personal email as a legitimate starting point; the
emailsselector and theemail domainselector are distinct rule types, and Cloudflare appends an implicit deny for everyone else. - The critical OTP caveat in Cloudflare's clientless-access best practices: pairing One-time PIN as a login-method include rule without an email or domain restriction lets anyone with any email address receive a code and get in. Our policy avoids this entirely — the include is the email itself, so OTP is only ever issued to the one allowed address.
- Cloudflare's OTP docs: each PIN is single-use, expires 10 minutes after request, a new PIN invalidates the prior one, and "Cloudflare only sends the email if the user is allowed by an Access policy" — blocked users get no email while the login page still claims one was sent. Also noted: if a user authenticates via OTP rather than the IdP, Access stops evaluating IdP group memberships (irrelevant to us — we have no groups).
- Changelog, 2026-06-18: new Zero Trust organizations now get the Cloudflare identity provider as the default login method instead of OTP; existing orgs keep their configuration and "can still add OTP or connect any third-party identity provider." The page makes no explicit security comparison — the direction of travel is the signal, not a stated verdict.
- Include rules are OR'd; Require rules are AND'd (Cloudflare One policy docs). With a single include and no require, our policy is exactly as narrow as it reads.
Convergences and contradictions
- Convergence: the live policy matches the proposal's authorization intent precisely — literal
ben@raydata.co, one include, no group, no bypass, no service-token backdoor on the HQ app. The May brief's worst case (domain-wide@raydata.co) is not present anywhere on the account. - Contradiction: the proposal and the May brief both describe the authentication factor as Google SSO with "MFA enforced at the Google account." Live, the only IdP is one-time PIN. The vault has been overstating the auth factor for three months: the real factor is control of the ben@raydata.co mailbox, and the MFA that actually protects HQ is whatever protects Google Workspace mail delivery — not a Cloudflare-enforced SSO hop.
- Convergence with the sibling brief: [[2026-07-16-fat-cat-board-router-token-scope.md]] found the
board-routertoken didn't exist; this check confirms the one service token that does exist (hq-frontend-to-api) is per-app scoped to hq-api and absent from hq.raydata.co. Both halves of the May service-token worry are now closed.
Synthesis for RDCO
CLOSE the item as asked. The question was literal-email or domain; the answer is literal-email, verified against raw API output, with zero access groups and zero bypass or non-identity policies on the HQ app that could widen it out of band. Item #1 on the May 11 "live discipline TODAY" checklist and follow-up #2 are both retired. No remediation command is warranted, and nothing was mutated in the course of checking. With the board-router item retired on 2026-07-16, the May brief's founder-action checklist now has only FileVault verification outstanding.
But the check surfaced a different, more interesting finding than the one it went looking for. The vault's stated security model for HQ — "Google SSO, MFA enforced at the Google account" — is not what is live. There is no Google IdP on the Zero Trust org at all. The actual chain is: request page → Access issues a 10-minute single-use PIN to ben@raydata.co → possession of that mailbox is the entire authentication factor. That is a materially different threat model from SSO. Under SSO, compromising Google mail alone is not enough; a phishing-resistant second factor stands between an attacker and HQ. Under OTP, mailbox compromise (or mail-forwarding-rule compromise, or a rogue mail-security scanner consuming the link — a documented Cloudflare failure mode) is HQ compromise. The blast radius is the founder's whole generative-UI substrate, including whatever the hq-api tunnel exposes off the Mac mini. This does not make the current setup bad — for a single-user internal dashboard with a literal-email include, OTP is a defensible posture and Cloudflare's docs treat it as supported — but the vault should describe it accurately, because a future decision ("can we let a contractor into HQ?", "should the CAF client demos live here?") will be reasoned from that description.
Recommended, not applied (read-only mandate): first, correct the record — [[2026-04-22-hq-proposal-v1.md]] and [[2026-05-11-cloudflare-tunnel-security-mac-mini.md]] both assert Google SSO and should be annotated with the live reality. Second, consider adding an IdP. Cloudflare made its own identity provider the default for new orgs on 2026-06-18 specifically because OTP was a weak default; adding the Cloudflare IdP (or Google Workspace SSO, which the proposal already intended) alongside OTP is additive and non-breaking — existing orgs keep OTP until they choose otherwise, and allowed_idps: [] would then accept either. That is a founder-gated change: it requires an OAuth dance the agent cannot complete, and it should be done with a second browser session open so a misconfiguration doesn't lock the founder out of his own dashboard. Third, a hygiene note orthogonal to the question: the hq-frontend-to-api service token expires in 2126. A 100-year non-expiring credential sitting in front of the Mac-mini API is worse hygiene than the literal-email policy is good; a 1-year expiry with a calendared rotation is the cheap fix.
The wider lesson repeats [[2026-07-16-fat-cat-board-router-token-scope.md]] almost exactly: the vault's security claims about Cloudflare Access all trace back to an April proposal document, and each one that gets checked against live config comes back partly wrong. Two for two now. Any remaining Access assertion in the vault should be treated as unverified until an API call says otherwise — and the API call takes under a minute.
Why this is in the vault
Closes follow-up #2 and retires live-discipline item #1 from [[2026-05-11-cloudflare-tunnel-security-mac-mini.md]], the security brief governing the Mac-mini Tunnel expansion of hq.raydata.co. It also corrects the authentication model recorded in [[2026-04-22-hq-proposal-v1.md]], which every future "who can reach HQ" decision (contractor access, CAF client demos, sharing a decision page) will otherwise be reasoned from incorrectly.
Open follow-ups
- Should RDCO add the Cloudflare identity provider or Google Workspace SSO to the Zero Trust org alongside OTP, given Cloudflare demoted OTP as the default for new orgs on 2026-06-18? Founder-gated (OAuth dance + lockout risk).
- The
hq-frontend-to-apiservice token expires 2126-03-30. Should we re-mint it with a 1-year expiry and add rotation to the quarterly Cloudflare hygiene cron? - Is FileVault actually on for the bedroom Mac mini? Still the last unverified item on the May 11 checklist — assignable to founder, 30 seconds.
ink.rdco.devgrantsakim@phdata.iostanding access to an rdco.dev site. Is that share still intended, or should quick-sites shares carry an expiry/review cadence?- Should a recurring job diff live Cloudflare Access apps/policies/IdPs against a committed expected-state snapshot, so config drift and the proposal-vs-production gap surface automatically instead of via ad-hoc research questions?
Related
- [[2026-05-11-cloudflare-tunnel-security-mac-mini.md]] — the parent brief; this closes its follow-up #2 and retires live-discipline item #1
- [[2026-07-16-fat-cat-board-router-token-scope.md]] — the sibling verification that closed follow-up #3; same proposal-vs-production failure pattern
- [[2026-04-22-hq-proposal-v1.md]] — the source of the "Google SSO" claim this brief corrects
- [[2026-05-01-fat-cat-council-architecture-proposal.md]] — asserts Access is "already in production" for hq.raydata.co; service-token context
- [[2026-04-30-ip-restricted-policies-tracker.md]] — the operational pattern for tracking policy-scoped access
- [[2026-04-26-rdco-web-app-deployment-standard.md]] — Cloudflare token rotation discipline the service-token hygiene note leans on
Sources
Live configuration (primary evidence) — read-only, 2026-07-22, via ~/.claude/scripts/cloudflare-api.sh (1Password item Cloudflare Ray Account API Key, vault Ray Agent), account 5d4a0efe3ae671cf9cf3265eb1ff738c:
cloudflare-api.sh whoamicloudflare-api.sh GET /accounts/<acct>/access/apps?per_page=50cloudflare-api.sh GET /accounts/<acct>/access/apps/c0c24227-b228-45bf-81df-45943b56ccd4(hq.raydata.co — target)cloudflare-api.sh GET /accounts/<acct>/access/apps/44f3e75d-298f-4cf8-bad0-08078ac78b2b(hq-api.raydata.co)cloudflare-api.sh GET /accounts/<acct>/access/apps/{ddd7fa5b…,31f6d6eb…,88aac54a…,49e1de4a…}(quick-sites / *.rdco.dev)cloudflare-api.sh GET /accounts/<acct>/access/groupscloudflare-api.sh GET /accounts/<acct>/access/identity_providerscloudflare-api.sh GET /accounts/<acct>/access/service_tokens
Vault
~/rdco-vault/06-reference/research/2026-05-11-cloudflare-tunnel-security-mac-mini.md~/rdco-vault/06-reference/research/2026-07-16-fat-cat-board-router-token-scope.md~/rdco-vault/01-projects/hq-raydata-co/2026-04-22-hq-proposal-v1.md~/rdco-vault/08-tooling/2026-05-01-fat-cat-council-architecture-proposal.md~/rdco-vault/08-tooling/2026-04-30-ip-restricted-policies-tracker.md~/rdco-vault/02-sops/2026-04-26-rdco-web-app-deployment-standard.md~/.claude/skills/cloudflare/SKILL.md— API wrapper + auth pattern~/.claude/skills/publish-site/SKILL.md— documents quick-sites as OTP-gated, consistent with the live IdP finding
Web
- Cloudflare One — Common Access policies
- Cloudflare One — Access policies (include/require/exclude semantics)
- Cloudflare Learning Paths — Clientless access best practices
- Cloudflare One — One-time PIN login
- Cloudflare Changelog, 2026-06-18 — Cloudflare identity provider is now the default for new accounts