Snapshots

A brand opens only to its own people, and staff see everything

Waiting on you
  • Eight roles, from root admin to demo visitor; outsiders see one wall pointing to the demo.
  • A brand's work email, a 14 day invite or a jam guest link gets you in.
  • Anyone at two whole staff domains can open every brand today.
  • Arata's Meta and Shopify are protected from disconnect unless a root admin forces it.
  • Every new route must pass the role check and the closed-store gate.
14day invite life
Your call

Limit staff to a named address list on production and turn on STAFF_REQUIRE_HD?

Security, privacy and legal · 9.6

Access rules

DraftLast checked Mon Oct 26Owner Param

Answer

Who opens what: our own staff see every brand, a brand opens only to its own work email, an invite or a single jam guest link, and everyone else is sent to the demo stores; this page settles those rules so no new feature adds a way in that skips them.

Today

The roles

Who How they get in What they may do Source
Root admin ADMIN_EMAILS; on production param@joinsquareup.com only Everything staff do, plus sign someone out everywhere and force a change to a protected connection brew/auth.js:89-91 brew/tools/prod-env.sh:9
Staff Any address at joinsquareup.com or shopos.ai, subdomains too Every brand, every view, every make; never claim a brand that is not at their own domain brew/auth.js:10-11, 33-36, 166
Brand admin, the owner First official email to tap Claim, connect an account or confirm the read Invite, set views, connect and disconnect, hand over; a brand always keeps one admin brew/auth.js:6-7, 159-171, 194-215
Member, same company Signs in with the claimed brand's own domain; joins on their own with every view; admins are emailed Everything but team changes; may invite others to their own views only brew/auth.js:104-109, 294-296
Member, invited An emailed invite, 14 days, valid only for that Google account The views the invite named brew/auth.js:78, 180-193, 264-276
Jam guest A shared jam link and a sign in Chat, edit the brief and make drafts in that one jam; never share, send, approve, assign, discard or make paid media brew/SCOPE.md:24 handbook content/engineering/changelog.md:58
Outsider Signed in with Gmail or another domain One wall pointing to demo.joinsquareup.com; cannot start a new read or sign up for Plays emails memory brew-access-rules
Visitor on the demo Anyone A throwaway sandbox of a prepared brand; makes are open there brew/auth.js:37-38, 148

What counts as the brand's own email

  • Same domain as the store, a subdomain of it, or the company domain the store sits under. brew/auth.js:27-31
  • Free mail and shared platforms such as myshopify.com, wixsite.com or vercel.app never count. brew/auth.js:16, 22-24
  • A brand our staff claimed for it, such as Arata, lets the store's own domain join. brew/auth.js:106-108
  • Starting a read gives nothing on its own since 23 Oct. brew/auth.js:139-140 memory brew-access-rules

Special cases

  • A closed Shopify store, read from its admin while behind the password page, is shown to its team and staff only; everyone else gets "This store isn't open yet". brew/closed-gate.js:1-12, 50-71 memory brew-closed-store-gate
  • Inside the Shopify admin, Shopify's session token names the person; the installer becomes admin of an unclaimed brand, the store owner becomes admin, other staff members. memory brew-shopify-app-review
  • Connecting accounts is for brand admins and staff only; teammates may sync. memory brew-real-connectors
  • Protected connections, PROTECTED_CONNECTIONS=arata-in, refuse disconnect, re-select or reconnect unless a root admin forces it. brew/connectors/protected.js:1-4 memory never-disconnect-arata-meta

Sessions and logging

  • Google sign in uses PKCE and a nonce; only a Google verified email is trusted. brew/auth.js:3-4, 220-238
  • The session is a signed cookie for 30 days with a session row; signing out ends it on every instance. brew/auth.js:243-244, 254-257 brew/sessions.js:1-4
  • A sign out link from another site does nothing. brew/auth.js:254-256
  • STAFF_REQUIRE_HD=1 would accept a staff address only from that domain's own Google Workspace; production does not set it. brew/auth.js:239-241 brew/tools/prod-env.sh:9
  • Every signed in email that opens a brand is logged with its access kind; only root admins read the list. brew/server.js:657-666, 817 handbook content/engineering/changelog.md:81

Decided exceptions

  • A personal Gmail stays on staging's admin list, by Param's choice on 23 Oct; do not flag it again. memory brew-access-rules

Tomorrow

Item Version Tag
Official email, invite or guest link as the only ways in v1 Stays
STAFF_REQUIRE_HD=1 on production v1 New, proposed
Staff by named address, not two whole domains v1 Changes, proposed
Guest links that expire and can be revoked v1 New, proposed
Proof of ownership by store admin or DNS, beside the email domain v2 New
Agency seats: one person on several brands, invited by each v2 New
Who may approve a post or a schedule, per brand v2 New
Who may set ad rules and undo a run v4 New

How we do it

  1. Never add a route, page or file that opens brand data without going through auth.roleOf, genOk and the closed store gate.
  2. Every new brand route falls under the closed gate's slugIn patterns. memory brew-closed-store-gate
  3. When touching auth, test the outsider paths: Gmail, another domain, a guest, signed out.
cd /Users/param/Documents/ChatGPT/brew && AI_MOCK=1 node --test test/access.test.js test/qa-access.test.js test/qa-final-access.test.js test/qv1-roles-security.test.js
  1. To see who opened a brand, a root admin calls GET /api/admin/visitors?slug=<slug>.
  2. To end someone's sessions everywhere, a root admin uses /api/admin/sessions.

Numbers

Metric Definition Where measured Status
Blocked opens Signed in emails refused per brand per week access_log, access blocked Proposed
Staff with full access Distinct staff emails seen in access_log access_log, access brew Proposed
Unclaimed partner brands Partners with no brand admin of their own members.json per brand Proposed

Open

  1. Should every shopos.ai address see every brand? Proposal: no; a named list of staff addresses on production, and the two domains only on staging.
  2. Turn on STAFF_REQUIRE_HD=1 on production? Proposal: yes, so a personal Google account made with a staff address never counts as staff.
  3. Should guest links expire? Proposal: 30 days, with a Revoke button for brand admins.
Sources
  • brew/auth.js:2-12, 16-36, 37-38, 89-119, 139-171, 180-193, 239-246, 254-257, 264-305
  • brew/closed-gate.js:1-75
  • brew/connectors/protected.js:1-4
  • brew/sessions.js:1-36
  • brew/server.js:657-666
  • brew/tools/prod-env.sh:9
  • brew/SCOPE.md:24
  • memory brew-access-rules, brew-closed-store-gate, brew-shopify-app-review, brew-real-connectors, brew-prod-backend, never-disconnect-arata-meta
  • handbook content/engineering/changelog.md:58, 81

Source file content/security-privacy-and-legal/access-rules.md · For agents: Markdown · JSON