Skip to content
Rickie Cruz
ActiveNext.jsTypeScriptSupabasePostgreSQLZohoResendVercelCloudflare

Chatter Snow

Board Member + Director of Technology and Media · Chatter Snow

Updated

Built the digital infrastructure for a growing LGBTQ+ ski and snowboard community organizing toward nonprofit status — then generalized it into a multi-tenant nonprofit operations platform, with Chatter Snow as its first tenant.

Problem

There was no infrastructure. A shared Gmail account and an Instagram profile were the entire operational stack — no domain, no organizational identity, no records, no system to track donated gear, coordinate volunteers, manage events, handle finances, or organize donations. Everything lived in DMs, threads, and individual people's heads, with no visibility for community members. Core issue: Chatter couldn't tell people "here's what we have available," and — more importantly for a group organizing toward nonprofit status — it couldn't tell anyone, including itself, what kind of impact it was actually having. A second problem surfaced once the system worked. Nothing about gear libraries, volunteer shifts, board minutes, or donation records is specific to snow sports, and other small organizations were solving the same problems with the same spreadsheets. But the code assumed exactly one organization: one tenant's domain was hardcoded as the platform's routing table, and every table held one org's rows with nothing separating them.

Context

Chatter Snow is not a nonprofit yet; the group is actively organizing to become one. That framing drove the whole project: the infrastructure exists so the organization can measure and evidence its impact — gear distributed, members served, events run, money in and out — which is exactly what a nonprofit application and future funders will ask for. Add a volunteer team, effectively no budget, a growing inventory of donated ski and snowboard gear, and an active community that wanted to know what was available. Being both a board member and the technical lead meant seeing the operational need and the technical constraints firsthand. What started as one organization's tooling is now a multi-tenant platform, and Chatter Snow is its first tenant — which also meant drawing an ownership line through the repository between the generic platform and Chatter Snow's own brand, copy, and data before either had commercial value.

Goals

  • Stand up real infrastructure — domain, email, identity, accounts — where none existed
  • Create a centralized inventory system
  • Enable the community to see available gear without a DM or an email
  • Capture the operational data needed to evidence impact for nonprofit formation
  • Serve more than one organization from one codebase, with each tenant's data isolated in the database rather than by application-layer care
  • Give every tenant its own domain, branding, and public copy without a fork or a rebuild
  • Make the system showable — a public demo anyone can walk through, with no real member data in it
  • Keep it maintainable by volunteers — not over-engineered

Constraints

  • Effectively no budget — a pre-nonprofit group with no funding stream
  • Starting from zero infrastructure, not migrating an existing system
  • Volunteer-driven — the system couldn't be complex
  • Real member, donor, and financial data in production: nothing from it could be copied into fixtures, tests, demos, or screenshots
  • No super-admin: platform staff can't hold a standing bypass into a customer organization's data
  • Multi-tenancy had to be retrofitted onto a live single-tenant system without an outage or a data migration people would notice
  • No dedicated devops/infrastructure team

Research & Discovery

As board member and director, the operational workflows, community expectations, budget reality, and volunteer capabilities were already well understood. Key insight at the start: with nothing to migrate, the risk wasn't legacy — it was over-building. Buy the commodity pieces (domain, mail, hosting), build only what's specific to Chatter, start narrow with inventory, and design for expansion. The multi-tenant work started from a different question: where would a second organization's data leak? The answer was "anywhere a developer forgot," which is why isolation was pushed down into the schema — a policy predicate on every table, a composite foreign key on every relationship, and a query that reports any table missing either.

Architecture

Core philosophy: minimal, maintainable, bought where possible, and generic by default. Two surfaces ship from one codebase — a public site and an operations portal — and both are now resolved per tenant. Next.js + TypeScript power both, backed by Supabase/PostgreSQL for inventory, events, volunteers, governance, and financials, with Zoho for organizational mail, Resend for transactional sending, Vercel for hosting, and Cloudflare for CDN and DNS. Multi-tenancy is a schema property, not an application convention: every tenant table carries a `tenant_id` with a default that resolves from the session, then the request host; every row-level security policy is scoped to the current tenant; every foreign key between two tenant tables is composite, so the database itself rejects a cross-tenant reference even from a service-role connection. The request `Host` is what decides which tenant a public page is for (matched against the tenant's custom domain, longest match wins), while the portal resolves the tenant from membership and never consults the host. Branding tokens and public site copy are per-tenant rows over platform defaults, so a new organization gets its own colors, logo, and page content without a fork. Deliberately not built: a custom CMS, a mobile app, and any super-admin role.

Design

Functional and clear, focused on usability over visual polish — volunteers needed to understand the system quickly. A tenant's public site tells its community who it is and what gear is available; the ops portal is where the organization actually runs — inventory, volunteer coordination and shift signup, events and registrations, governance (board meetings, minutes, decisions, conflict-of-interest disclosures), donations, expenses, and reimbursements. Each organization edits its own copy, photos, and brand tokens from Administration rather than filing a ticket. Those same tokens render each tenant's own brand guide at /brand on its public site, so the reference a volunteer or a designer is handed is generated from what the product actually renders with — it cannot go stale, and nobody maintains it by hand. Access is role-based, with the permission matrix itself editable per tenant, so an organization decides what its own roles can do.

Implementation

Phase zero was infrastructure that didn't exist: registering chattersnow.org, moving off the shared Gmail account to organizational email, and setting up DNS, hosting, and accounts. The initial build covered inventory management, the community-facing gear view, and basic volunteer coordination, then grew into the full ops portal — events, governance, donations and financial tracking, volunteer hours, and reporting. The multi-tenant conversion ran in phases against the live system: tenancy tables and membership first, then a `tenant_id` column and scoped policy on all 73 tenant tables plus 110 composite foreign keys, then host-based tenant resolution, then provisioning, per-tenant branding and site content, per-tenant data-retention rules, export and deletion, and finally a platform administration page so provisioning a tenant no longer needs a shell. The order was load-bearing: the first tenant's custom domain had to be set and verified before a second tenant went active, because a "sole active tenant" fallback was silently holding the public site together until then.

Challenges

  • Starting from zero — no domain, no org email, no records to migrate or learn from
  • Requirement creep — once people saw the system working, everyone wanted new features
  • Designing for volunteers — complex features had to stay simple to use
  • Retrofitting tenancy onto a live database: a single forgotten policy or single-column foreign key is a silent cross-tenant leak, so the schema had to police itself
  • Cross-tenant controls that were harmless with one tenant: data-retention purges and role administration were platform-wide, meaning any organization's admin could act on every organization's data
  • Showing the system without exposing anyone: a demo needs an admin account that an anonymous stranger holds, which turns every remaining cross-tenant control into a public one
  • Keeping the platform generic while it is also one specific organization's production system

Decisions

Isolation lives in the database, not the application

Scoped every row-level security policy to the current tenant and made every foreign key between tenant tables composite, rather than filtering by tenant in queries.

Tradeoffs: A cross-tenant read or reference is rejected by Postgres whatever writes it — including a service-role script or a future bug — and a diagnostic query fails the build if any table is missing either guard. The cost is a heavier schema: composite keys change how embedded queries have to be written, and every migration has to seed per tenant.

No super-admin — support access is granted by the tenant

Platform staff hold a time-boxed support membership that the customer organization issues and revokes, instead of a role that bypasses tenant scoping.

Tradeoffs: Every policy stays exactly one shape, so the isolation test suite covers platform access too and there is no privileged path outside it. The cost is that support can't self-serve into an account to debug it — someone at the organization has to open the door, with a reason and an expiry.

The host decides the tenant for public pages; membership decides it for the portal

Public requests resolve a tenant from the request Host matched against custom domains; portal requests resolve it from the signed-in user's membership.

Tradeoffs: One domain column serves a tenant's site and portal subdomains together, and a forged host header can only reach the public surface a visitor could reach anyway. The cost is an operational step per tenant — domains registered with the host and the auth redirect allowlist — that can't be fully automated.

Ship the demo as a real tenant, not a demo mode

The public demo at demo.rickiecruz.com is an ordinary tenant on a demo plan with seeded fictional data, rebuilt from scratch nightly, rather than a special code path.

Tradeoffs: Anyone can walk through the whole ops portal in one click without an account, and the demo exercises the same policies as a paying tenant instead of a parallel path that could drift. The cost is that the demo's admin is effectively an anonymous stranger, so every control still reaching outside a tenant had to be closed before it could go live — which is the work that made the platform genuinely multi-tenant.

Separate the generic platform from the organization's own material

Drew an ownership line through the repository: the generic nonprofit-operations platform on one side, Chatter Snow's brand, copy, content, and data on the other, with production data never crossing into fixtures or demos.

Tradeoffs: Chatter Snow keeps its own identity and records outright and the platform stays reusable, which is what makes serving a second organization possible at all. The cost is discipline in every commit — no organization name hardcoded in platform code, no snow-sports assumption baked into the schema.

Use Supabase, not Firebase

Chose Supabase over Firebase for the database layer.

Tradeoffs: Better suited to complex relational data (gear, volunteers, events, governance, and financials are all interconnected), and Postgres row-level security is what later made tenant isolation enforceable in the database — at the cost of slightly more infrastructure to manage than Firebase.

Buy the commodity, build the difference

Introduced Zoho as the org's email rather than building or self-administering anything, so the custom work stayed on what's specific to Chatter.

Tradeoffs: Replacing the shared Gmail account with a real organizational mailbox took a day instead of a project, at the cost of a small recurring bill on a near-zero budget.

Public site and ops portal are separate surfaces

Split the community-facing site from the operations portal, on separate hosts.

Tradeoffs: Simple, focused experience for members and an easier boundary to secure and scale, at the cost of some code duplication.

Vercel serverless functions, not a traditional backend

Used Vercel serverless functions for all API logic instead of a standalone backend service.

Tradeoffs: Simple scaling, low cost, and minimal devops, at the cost of cold-start latency — acceptable for a nonprofit's usage patterns.

Result

A group that ran on a shared Gmail account and an Instagram profile now has a domain, organizational email, a public site, and an operations portal covering inventory, volunteers, events, governance, donations, and financials. The community can see available gear without sending a DM, and for the first time the organization is generating the operational record it needs to show its impact as it organizes toward nonprofit status. What was one organization's tooling is now a multi-tenant platform: a new organization can be provisioned with its own domain, branding, copy, roles, and retention rules, its data isolated at the database level, and it can export or delete everything it owns. Chatter Snow is tenant one, and a fully seeded demo of both surfaces is public at demo.rickiecruz.com. Specific impact figures are still TBD, pending more usage data.

Lessons Learned

  • Infrastructure is the measurement — Chatter can't evidence its impact for nonprofit formation without a system that records what it does
  • Start narrow, design wide — focusing on inventory first made launch possible, but the architecture allowed for expansion all the way to multi-tenancy
  • Push isolation as low as it will go — a rule enforced by the database holds even when a developer, a script, or a future refactor forgets it, and a rule enforced by convention doesn't
  • Every shortcut that was fine with one tenant becomes a vulnerability with two — a platform-wide purge job or a global admin control is invisible until the day it isn't
  • Building a demo is a security exercise, not a marketing one — handing an anonymous visitor an admin account is the most honest audit of tenant isolation there is
  • Draw the ownership line before the work has value — separating the generic platform from the organization's own material is cheap to do early and expensive to untangle later
  • Get hands dirty — being on the board meant understanding real problems firsthand, an edge an outside consultant wouldn't have

See it running

  • demo.rickiecruz.com — a seeded demo tenant's public site. Everything on it is fictional: no person, donation, or record there describes anybody.
  • demo.rickiecruz.com/portal — the operations portal demo. One click signs you in as an administrator of that demo organization, with no account needed. Change whatever you like; the whole tenant is torn down and rebuilt from scratch every night.
  • chattersnow.org — the first tenant in production: who Chatter is, and what gear is available to the community. Its ops portal at portal.chattersnow.org holds real member and financial data, so access there is invite-only and role-based. The demo above is the way to see the system.

Metrics

Technical metrics (deployment time, uptime, page load time, hosting cost) and organizational impact metrics (time saved, member engagement, admin efficiency gains) are still TBD — they'll be added here once more usage data is available. Producing those impact numbers is a large part of why the platform exists: Chatter is organizing toward nonprofit status, and the operational record the portal generates is what that case will be built on.

What's next

Near-term: onboard a second real organization and let the provisioning path prove itself against a customer rather than a fixture. Medium-term: reporting and analytics aimed squarely at the nonprofit application — gear distributed, members served, events run, money in and out — and tenant-level self-service for the operational steps that still need an operator, like domains and verified sending addresses. Long-term: potentially a mobile app and fundraising integrations.