Answer
Connectors read a brand's own accounts, read only, encrypt every token, refresh on a fixed cadence per account, and make the team's numbers the source of truth for the team only; Arata's live connections are protected in code and are never disconnected.
Today
The accounts
| Connector | How it connects | Scopes or access | Read every | Source |
|---|---|---|---|---|
| Shopify | The brand's own app (client id and secret) through own-oauth; the public app "Brew" is under review |
read_products,read_inventory; read_orders,read_customers with protected-data approval or the own app |
3 h | brew/connectors/shopify.js:11-13, 92 brew/connectors/index.js:37 memory brew-shopify-connector-qa |
| Meta Ads | Facebook Login for Business on SquareUp's Meta app (config 2157080375167128), one or more ad accounts | ads_read, business_management; Graph v26.0 |
3 h | brew/connectors/meta.js:1-12 brew/deploy.sh:39 |
| Google Ads | Google sign-in with the developer token (GOOGLE_OAUTH=1) |
adwords, analytics.readonly, openid, email |
6 h | brew/connectors/google.js:18 memory brew-google-oauth-scopes |
| Google Analytics | Same sign-in, or a property ID with Brew's service account added as Viewer | analytics.readonly |
6 h | brew/connectors/google.js:29, 182 |
| Klaviyo | A pasted read-only private key | lists, sizes, the five flows | 12 h | brew/connectors/klaviyo.js:1-3 |
| Judge.me | OAuth or the store's token | read_reviews read_shops |
6 h | brew/connectors/reviews.js:1-14 |
| Slack | OAuth on the Brew Slack app | chat:write, chat:write.public, incoming-webhook, channels:join, channels:read, groups:read, channels:history, groups:history, users:read, users:read.email, team:read |
event-driven | brew/connectors/slack.js:11 |
| Marketplaces | Off; shown as "Coming soon" | brew/connectors/index.js:32 brew/DUMMY.md |
Slack is never evidence: no cards, no ledger. brew/connectors/index.js:5-7
Freshness and failure
- A member opening the brand re-reads an account due within
OPEN_H(Shopify and Meta 1 h, Google and GA4 and Judge.me 2 h, Klaviyo 6 h). brew/connectors/index.js:40, 315 - A failed read waits for the platform's Retry-After, else doubles from 15 minutes. brew/connectors/index.js:44-50
- After 3 failures in a row, or once the last good read is older than four cadences (at least 24 hours), numbers are marked stale and stop being evidence. Owners and admins hear a week before a Meta sign-in ends. brew/connectors/index.js:51-60
- A new connector needs entries in
EVERY_H,OPEN_H,NAMESandPRECEDENCE. memory brew-connected-facts
Who sees what
- Tokens are AES-256-GCM in
private/vault/, never sent to the page, never logged. brew/vault.js:1-3 - Account reads live in
private/live/<id>.json; numbers reach only members. A visitor never gets one. brew/connectors/index.js:1-4 memory brew-connected-facts - On the fact sheet, connected numbers beat the site read for members; demo and stale rows never count. memory brew-connected-facts
- Only admins (owner or staff) connect or disconnect; teammates can sync. memory brew-real-connectors
- A GA4 property already used by another brand, or whose web stream is not the brand's site, is refused unless staff override; 6 tries per person per 15 minutes. brew/SCOPE.md
Protected connections
PROTECTED_CONNECTIONS (default arata-in, set on production) refuses disconnect, re-select, a new key or a reconnect unless a root admin forces it; the vault refuses to drop the token too. brew/connectors/protected.js brew/connectors/index.js:148-150, 267-269, 429 brew/tools/prod-env.sh:9
Rules around it:
- Never call
DELETE /me/permissionson disconnect: one Meta grant may serve several brands. memory brew-meta-connector-qa - Disconnect tests run on Cozy only. memory never-disconnect-arata-meta
- Arata's Shopify token (own app) never expires and Meta has no refresh, so the same vault serves both sites; connector reads run on both. memory brew-prod-backend
What a connection changes
The first connected account rewrites every Play privately (cap $40); later accounts rewrite only the Plays they touch. Best sellers become heroes in the team's view, and readings fill the health rings. memory brew-connect-refresh brew/on-connect.js:1-3
The demo
On the demo every connector is simulated from the brand's public read, tagged Dummy, and never evidence. brew/DUMMY.md brew/connectors/index.js:67
Approvals
| Platform | State | Source |
|---|---|---|
Verified 21 Oct for adwords and analytics.readonly; a new scope re-triggers review |
memory brew-google-oauth-scopes | |
| Shopify public app | Submitted 21 Oct; after approval deploy with SHOPIFY_OAUTH=1 and the install URL |
memory brew-shopify-app-review |
| Meta App Review | Draft filled 24 Oct; blocked on business verification and two screencasts | memory brew-meta-app-review |
Both sites' callbacks (/auth/google/callback, /connect/<id>/callback) are registered with Google, Meta and Slack; Slack events go to production, which relays workspaces it does not serve to staging. brew/.wt-prod/docs/production.md
Tomorrow
| Item | Version | Tag |
|---|---|---|
| Read only, every connector | v1 | Stays |
| One-click Shopify through the public app | v1 | Changes after Shopify approves |
| Meta for any brand (out of development tier) | v1 | Changes after App Review |
| Connections and secrets in their own table | v2 | New |
| Marketplace seller APIs | v3 | New |
| Write scopes (post, pause, change budgets) | v3 | New, only behind a person's yes per action |
How we do it
- Audit what Brew shows against the platform, read only:
node tools/meta-audit.mjs <slug> # needs FEED_VAULT_KEY and the Meta app secrets
node tools/shopify-audit.mjs <slug> # needs FEED_VAULT_KEY
memory brew-meta-connector-qa memory brew-shopify-connector-qa
- Check that connectors still read on both sites:
gcloud run jobs execute brew-prod-ping --region=asia-south1 --wait
gcloud run jobs execute brew-staging-ping --region=asia-south1 --wait
memory brew-prod-backend
- Any script touching
private/vault/meta.json,connections.jsonor disconnect must skiparata-in. memory never-disconnect-arata-meta - Do not hammer syncs; Meta throttles "too many calls to this ad-account". memory brew-brand-book
- Bump
META_GRAPH_VERSIONandGOOGLE_ADS_API_VERSIONwhen the platforms announce a sunset. memory brew-meta-connector-qa memory brew-real-connectors
Numbers
| Metric | Definition | Where measured |
|---|---|---|
| Live connections | Connections in state live, per brand | connections.json; db/status.mjs |
| Stale connections | Connections marked stale | same |
| Read success rate | Good reads over all reads per connector | Proposed (from connections.json fails) |
Open
- When should Marketplaces become real? Proposal: after Meta App Review, starting with Amazon seller data for India.
- Who besides Arata goes on the protected list? Proposal: every design partner once their first account connects.