The Marketing Show System map

Agency architecture · one plan, many rooms

Every client gets their own room. You buy the only plan.

One Claude Max subscription sits with the agency owner. A single bot carries that credential into a Discord server, gives every client and every teammate the same shelf of skills, and lands the actual work as commits in private GitHub repos. Each skill runs against one client’s context and nobody else’s.

Claude Max
Discord
GitHub
$200per month, total
1credential
7people served
12shared skills
4context stores
12connected accounts
3storage systems
Agency owner @owner · billing + admin
Claude Max 20× $200 / mo
3 client rooms 4 internal teammates
Agency bot holds the key

The only thing in the server allowed to call Claude. Runs on a small VPS as agency-bot; the API token never leaves that box and never appears in a message.

deploys from agency/agency-bot on push to main
Skills written once — every room can run every one of them method, not data
Publish
  • /write-blog-post
  • /post-to-gbp
  • /social-post
  • /send-newsletter
Respond
  • /reply-to-reviews
  • /follow-up-lead
  • /answer-faq
Produce
  • /repurpose-video
  • /draft-ad
  • /build-landing-page
Report
  • /monthly-report
  • /rank-check
Agency HQ discord server · 2 categories
7 online
Client workspaces private — one room per account
sealed
Summit Karting
#summit-workspace
ran here
/post-to-gbp2h ago
/reply-to-reviewsyesterday
2 client staff
1 account mgr · agency-bot
Summit’s own 3 accounts
summit.context
index1,240 rows
facts410 KB
assets6.1 GB
  • brand voice
  • ad accounts
  • lap data
sealed
Handy Home Co.
#handy-workspace
ran here
/write-blog-post40m ago
/follow-up-lead3h ago
1 client staff
1 account mgr · agency-bot
Handy’s own 3 accounts
handy.context
index2,880 rows
facts380 KB
assets9.4 GB
  • rate card
  • job photos
  • review replies
sealed
Bright Exteriors
#bright-workspace
ran here
/draft-ad1h ago
/monthly-reportMonday
3 client staff
1 account mgr · agency-bot
Bright’s own 3 accounts
bright.context
index1,960 rows
facts520 KB
assets12.7 GB
  • install gallery
  • territories
  • quote history
Internal one shared brain
staff only
Employee rooms
#ops  #creative  #ask-claude
ran here
/monthly-reportthis morning
  • @media-buyerfull
  • @content-leadfull
  • @account-mgrfull
  • @va-opsfull
the agency’s own 3 accounts
agency.context
index640 rows
facts1.2 MB
assets2.3 GB
  • SOPs
  • templates
  • past wins
Where it actually lives three systems, one job each — nothing is stored twice per client, sealed
Postgresthe index

One table, one row per asset and per job, client_id on every row. That column is what makes the seal enforceable in code rather than by habit.

  • what it is, where it lives, when it landed
  • the only layer you query
  • one database for the whole agency
~200 bytes a row · 10,000 assets is a few MB
GitHubthe facts

One private repo per client. This is the layer the model actually reads, and the layer a human can review before it ships.

  • agency/summit · agency/handy · agency/bright
  • agency/handbook · agency/agency-bot
  • context.md, rate card, SOPs, the site itself
kilobytes · diffable, reviewable, revertable
Cloudflare R2the assets

Every original: before and afters, phone video, raw uploads. One prefix per client, keyed by job. Never in git, never in the database.

  • ath/jobs/2026-08-21-swing-build/before-01.jpg
  • zero egress, so serving them is free
  • git refuses files past 100 MB anyway
~66 MB a job · roughly 10 GB a year, per client
Google Drive is the doorway, not the house

Clients drop files where they already know how. A watcher files them into R2 and writes the index row. Nothing of record lives in Drive.

The server keeps nothing

It receives the upload, processes it, pushes the originals, commits the facts, writes the row. If it dies you rebuild it in an hour and lose nothing.

One skill, three different answers

A skill carries the method — the steps, the format, the standard you hold the work to. It carries no facts about anybody and no keys to anything. The facts come from whichever room it was run in, and so do the accounts it is allowed to publish to. Same command, different work, landing in a different place every time.

/write-blog-post × the room it runs in = a different post every time
summit.context
“Why our heats run nine minutes, not five”

High energy, written for parents. Pulls last season’s lap data and the two questions that come up at the counter every Saturday.

lands on Summit’s listing and Summit’s page — nobody else’s
handy.context
“What a water heater swap actually costs”

Plain and price forward. Pulls the current rate card and photos from three jobs finished last month, because this audience wants the number before the story.

lands on Handy’s listing and Handy’s page — nobody else’s
bright.context
“Book December lights in September”

Premium and seasonal. Pulls the install gallery and the territory calendar, so the offer only names neighbourhoods with crews still open.

lands on Bright’s listing and Bright’s page — nobody else’s

Why it is not one big database

The instinct is to put everything in one place. The numbers argue against it, and they argue in a specific direction: split by what the data is, not by which vendor you like.

small and queried Goes in Postgres

The index. A few hundred bytes a row, and the only thing you ever run a query against. One database for the whole agency, with client_id doing the isolation.

small and read Goes in GitHub

The facts. This is what the model reads and what a person reviews, so it wants history, diffs, and a revert button. A year of one client comes to single-digit megabytes.

large and served Goes in object storage

The originals. Sixty-six megabytes a job, ten gigabytes a client a year. Blobs in a database is the classic mistake, and git rejects anything past a hundred megabytes.

the tempting shortcut Not one database per client

Per-client databases buy isolation you already get from a filter, and cost you a set of migrations, backups, and credentials per account. Revisit it when a contract demands it.

What happens when someone types

The same seven steps run whether the sender is a client or a teammate. Only step 3 changes, and it is the step that keeps accounts apart.

  1. Someone runs a skill

    /write-blog-post in a channel. Client or employee, no difference. No app to install, no login to hand out.

  2. The bot reads the channel ID

    That ID alone decides whose room this is. Identity comes from the room, not from a password.

  3. It loads exactly one store

    Only summit.context is in scope inside #summit-workspace. The other stores are not reachable from here.

  4. The skill supplies the method

    Same steps and same standard in every room. Fix the skill once and all four rooms get the better version.

  5. The call goes out on the owner’s plan

    One credential, one invoice. Nobody downstream sees a key or gets a bill.

  6. The answer posts, the store grows

    The reply lands in the thread and the new detail is written back, so tomorrow’s answer starts smarter.

  7. Real work becomes a commit

    Copy, pages and configs land in agency/summit as a pull request the account manager can read before it ships.

Six rules that make it hold up

the credential Never leaves the bot

Clients and staff get capability, not access. Removing someone is a Discord permission change, not a key rotation.

the stores One per account, sealed

A store is bound to its channel. If a request cannot name the room, it cannot read the memory — which is what makes competing clients safe in the same server.

the rooms Adding a client is adding a channel

New account, new channel, new empty store, new repo. No new subscription, no seat math, no onboarding call about which tool to install.

the skills Method here, facts there

A skill never stores anything about a client, which is exactly why it is safe to share one across competitors. Improve /reply-to-reviews on a Tuesday and every account gets the better version on Tuesday.

the connectors One set of keys per room

A connector is a credential to something the client already owns — their listing, their page, their sub-account. It is sealed exactly like the context, and it lives encrypted in the index, never in a repo. /post-to-gbp is one skill; which listing it posts to is decided entirely by the room it was run in.

the record Chat is not the archive

Discord is where the conversation happens; GitHub is where it survives. Every answer worth keeping becomes a diff you can review, revert, and hand to the next person.

Take it with you

The editable source is a draw.io file — open it at diagrams.net, drag the boxes around, and swap the client names for your own.