Skip to content

Instantly share code, notes, and snippets.

@ahoward
Created April 14, 2026 01:28
Show Gist options
  • Select an option

  • Save ahoward/c0df9d51637bd017b01e40be72bd9081 to your computer and use it in GitHub Desktop.

Select an option

Save ahoward/c0df9d51637bd017b01e40be72bd9081 to your computer and use it in GitHub Desktop.
VRPS Information Architecture — Product Level

VRPS Information Architecture

Product-level design. Produced through 3 rounds of adversarial peer review.


The Big Picture

VRPS is organized as a simple hierarchy. Everything flows from the top down. Ownership and authorization are always explicit — never inferred.

Customer
  └── Property
        ├── Units        (residential units, retail spaces, offices)
        │     └── Managers     (people who manage the unit)
        │     └── Lot Access   (which lots this unit can issue passes for)
        └── Lots         (physical parking areas)
              └── Pass Types   (what kind of passes exist here)
                    └── Offers       (how passes are made available)
                          └── Passes       (a consumer's actual pass)

Consumer (the person parking) sits outside this hierarchy — they hold passes, own vehicles, and interact with lots. They are not part of the ownership tree.

Enforcement is an observer — it reads the tree (is this plate authorized?) but never modifies it.


The Entities

Customer

The top-level organization. A property management company, a real estate group, a building owner. Everything belongs to a customer. Billing happens at this level.

Examples: Acme Property Management, Harbor Group, Downtown Parking LLC


Property

A distinct physical location. A building, a complex, a campus. Belongs to one customer. A customer can have many properties.

Properties have their own branding (logo, colors) that appears on the lot landing page and consumer-facing materials.

Examples: Harbor View Apartments, The Meridian Office Complex, 400 Main Street


Lot

A physical parking area. Belongs to one property. A property can have many lots.

Each lot has a capacity, a mode (public / private / both), and a unique QR code that never changes once printed. Passes are always issued for a specific lot.

Examples: Harbor View Main Lot, P2 Underground, Visitor Surface Lot

Peer Lots: A lot can be made accessible to units from another property — for example, a shared surface lot between two adjacent buildings. The lot still belongs to one property (its owner). Access is granted explicitly, not inherited.


Unit

A named group of passes within a property. This is the operational unit that property managers work with day-to-day.

In residential buildings, a unit is literally an apartment unit — Unit 4B, Unit 12A. In commercial properties, it might be a retail tenant, an office suite, or a parking membership. In mixed-use properties, it's whatever the operator defines.

Units have:

  • A name (e.g., "Unit 4B", "Retail Suite 101", "Visitor Account")
  • A maximum number of concurrent active passes
  • One or more managers (the people authorized to issue passes for it)
  • Explicit access to one or more lots (declared, not assumed)

A unit in one property can be granted access to a peer lot from another property — that access is explicit and audited.


Manager

A person authorized to manage a unit. They can issue passes, view pass activity, and manage vehicles for their unit. A manager is a platform user (has a login). A unit can have multiple managers; a manager can manage multiple units.


Pass Type (Family + Template)

Defines what kind of pass exists at a lot. This has two layers:

  • Pass Type (called a "family" technically) — the named product. "Monthly Resident Pass", "Visitor Day Pass", "Employee Annual". This is what operators name and manage. It belongs to a lot.
  • Terms (called a "template" technically) — the specific rules: rate, duration, seasonal restrictions. When a pass type's rate changes, new terms are created. Existing passes keep the terms they were issued under. Terms are immutable once a pass has been issued against them.

Operators manage pass types. The terms are the implementation detail.


Offer

How a consumer gets access to a pass. An offer is a standing authorization attached to a pass type. There are four kinds:

Offer Type How it works
Open Any consumer at the lot can claim a pass. No code needed. Used for public lots.
Claim Code Consumer enters a code to claim a pass. The manager distributes the code. Codes can be rotated.
Group A block of passes with a share link and a capacity cap. Used for events, bulk resident move-ins, etc.
Direct A manager issues a pass directly to a specific consumer. No code needed.

Offers can have:

  • A capacity limit (e.g., only 20 passes from this offer)
  • A validity window (valid from / valid until)
  • An expiry date
  • A host consumer (for group offers — the person who shares the link)

When an offer is fully claimed, it is automatically marked exhausted. Operators can revoke an offer at any time.


Pass

The actual parking authorization held by a consumer. A pass is issued when a consumer claims an offer. At the moment of issuance, the current terms (rate, duration) are locked onto the pass — they do not change if the pass type's terms are later updated.

A pass belongs to:

  • A consumer (the person parking)
  • A vehicle (the plate that will be parking)
  • A lot (where they're authorized to park)
  • A unit (if issued via a unit's offer)

Pass statuses: active → expiring soon → expired. Can also be revoked, suspended, or abandoned.


Consumer

The person parking. Not an operator — consumers are residents, visitors, employees. They have:

  • One or more identity methods (phone, email) — either can be used to look them up
  • One or more vehicles
  • A history of passes

Consumers are identified by phone or email. Either works — no single method is required.


Vehicle

A specific plate registered to a consumer. A consumer can have multiple vehicles and designate one as their default. When a pass is issued, it is linked to a specific vehicle. Enforcement looks up the plate to check authorization.


Enforcement

A third-party patrol company. Scoped to one or more properties (and optionally specific lots within those properties). Officers look up plates and log scan events and violations. They cannot issue or modify passes.


Key Design Decisions

The hierarchy is a tree, not a graph

Every entity has exactly one parent. A lot belongs to one property. A property belongs to one customer. Authorization always flows up the tree — you own something if you own its ancestor. There are no shortcuts or parallel paths.

Peer lots without breaking the tree

A lot is owned by one property. But it can be made accessible to units from another property — a shared lot between two adjacent buildings, for example. This is an explicit access grant (audited, reversible), not a change of ownership. The lot's parent property never changes.

Offers unify three old patterns

Previously, passes could be acquired three different ways (direct issuance, claim codes, group share links), each with its own data model and behavior. These are now unified under a single "offer" concept with four typed variants. One model, one audit trail, one UI pattern.

Pass types and terms are separate

Operators manage pass types (the named product). The underlying terms (rate, duration) are versioned and immutable once used. If you change the rate, new terms are created. Old passes are unaffected. This gives operators flexibility while protecting consumers from retroactive changes.

Units declare their lot access explicitly

A unit doesn't automatically get access to all lots in its property. Access is declared via an explicit assignment. This makes it possible to have a unit that covers a peer lot from another property, and makes authorization auditable — you can always see exactly which lots a unit can issue passes for.

Consumers are separate from operators

The people who park (consumers) are an entirely different entity from the people who manage properties and units (operators). They have different identity models, different auth flows, and different data. They don't share a users table.


What Is Not In Scope (Yet)

  • Full role-based permissions: operators currently have a simple role set. Granular per-action permissions are a future iteration.
  • Cross-customer operators: a manager who manages units across multiple customers' properties. Supported in the future via a junction table.
  • Availability engine peer lot support: the real-time capacity engine needs an update before peer lots go live. The schema supports it; the engine logic follows.
  • Consumer merge tooling: UI for merging two consumer records that turn out to be the same person.
  • Full billing engine: the financial data model is in place (transactions, payouts). The application logic for invoicing and reconciliation is a separate workstream.

Entity Summary

Entity Belongs To Key Attributes
Customer Name, billing info, Stripe account
Property Customer Name, address, timezone, branding
Lot Property Name, capacity, mode, QR slug
Unit Property Name, max passes, lot access
Manager Unit User, status
Pass Type Lot Name, description
Terms Pass Type Rate, duration, seasonal rules (immutable once used)
Offer Lot + Pass Type Type, code, capacity, validity window
Pass Consumer + Vehicle + Lot Status, start/end, terms snapshot
Consumer Identity methods (phone/email), vehicles
Vehicle Consumer Plate, state, default flag
Enforcement Company Name, property/lot scope
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment