What it is live
TimeKeep is the SPS internal timekeeping system. Contributors log hours against projects, projects have recursive Work-Breakdown-Structure (WBS) trees with per-node budgets, and managers can see budget burn-down per node and per person. Live at time.sandpointstudios.ltd, behind Cloudflare Access.
It's the internal mirror of the metering pattern used for billing JV products like HaulHard (hourly) vs Wyrdlyre (profit-share) — the same person can have different roles in different orgs and the time logged against each is segregated. Drives downstream payroll (Gusto) and accounting (Vigil Ledger) sync.
Architecture decisions locked
| XDEC-128 (2026-05-17) | Multi-tenant orgs + memberships. Same user can have different roles per org. Platform-admin view + per-user org switcher. |
| XDEC-129 (2026-05-17) | WBS — projects + recursive WBS trees + per-(node, member) budgets + strict leaf-only allocation. An auto-seeded "Misc" leaf catches entries that don't fit any specific WBS node. |
| Payroll pick (2026-05-17) | Gusto Simple ($40 + $6/employee/month) chosen over QuickBooks Payroll on API quality + UX. Drives the Phase 2 sync. Teagan owes the Gusto signup + API token mint before Phase 2 build. |
Topology
Google IdP] ACC --> TK[time.sandpointstudios.ltd] TK --> APP[timekeep container
vigil-server :8016
FastAPI + React in one image] APP --> PG[(Postgres
multi-tenant orgs)] subgraph FUTURE[Planned sync layers] GUSTO[Gusto API
P2] LEDGER[Vigil Ledger API
P3] end APP -.->|hours → payroll| GUSTO APP -.->|hours+budgets → GL| LEDGER
Hosting
| Code | /opt/timekeep/ on vigil-server |
| Stack | FastAPI + Postgres + React, all in one docker-compose image |
| Local port | :8016 (fronted by cloudflared) |
| Public URL | time.sandpointstudios.ltd |
| CF Access app id | ce99b9da-... |
| DB credentials | BW SPS Service item 2aff9f8b-... |
| Redeploy | git pull && docker compose up -d --build from /opt/timekeep/ |
Data model
Multi-tenancy (XDEC-128)
- Orgs — top-level tenants. Each SPS-side product/entity gets one (SPS, OneCut Studios LLC, FinishOps LLC, Agoge Development LLC, etc.).
- Memberships —
(user, org, role). The same user can hold different roles in different orgs (Lew is hourly in HaulHard but profit-share in Wyrdlyre — both tracked here, separately). - Per-entry rate override — most entries inherit from
memberships.default_rate, but a single entry can pin its own rate (for one-off contracted work). - Edit audit — every change to an entry is logged. No silent edits.
- Paid tracking — entries flip to "paid" once Gusto sync confirms a payroll run included them. P2 work.
WBS (XDEC-129)
- Projects belong to an org.
- Each project has a recursive WBS tree — nodes can have child nodes arbitrarily deep.
- Per-(node, member) budgets — Lew's budget against HaulHard's "Driver onboarding flow" node is separate from Raghav's budget against the same node.
- Strict leaf-only allocation — time entries must hit a leaf node, never an internal node.
- Auto-seeded Misc fallback — every project ships with a "Misc" leaf so contributors aren't blocked when the right specific leaf doesn't exist yet.
Dependencies
- Cloudflare Access + Google IdP — the door.
- cloudflared tunnel on vigil-server — exposes time.sandpointstudios.ltd.
- Postgres — TimeKeep's own DB inside the compose stack (not the shared vigil-postgres).
- Gusto (planned P2) — payroll sync target.
- Vigil Ledger (planned P3) — accounting sync target.
Roadmap
- Owed now: Teagan signs up for Gusto Simple, mints API token. Stored in BW SPS Service. Blocks Phase 2.
- Phase 2: Gusto sync — push hours → Gusto pay periods, pull back paid-status, flip entries to "paid."
- Phase 3: Vigil Ledger sync — push hours + budget burn to the GL so accounting sees both labor cost and project profitability in real-ish time.
- Reports: burn-down per node + per member, budget vs actual, profitability-per-product.
- Per-product rate cards: as JV-partner products evolve (Wyrdlyre profit-share has different mechanics than HaulHard hourly), encode the difference in the rate-resolution logic.
Why this matters cross-portfolio
TimeKeep is the substrate that makes "contributor X spent N hours on product Y this month, at rate R, with budget remaining B" a queryable fact rather than a memory exercise. Several downstream concerns depend on it:
- Payroll accuracy. W-2 contributors (Raghav inbound 2026-06-08) need accurate hours.
- JV profit-share math. Lew's Wyrdlyre profit-share vs HaulHard hourly only works if the time entries are separated by org.
- Per-product P&L. Each LLC entity (SPS, OneCut Studios, FinishOps, Agoge Development) needs labor allocated to its books separately for tax / accounting purposes.
- Budget enforcement. The recursive WBS lets a project owner see when a specific work package is over budget without rolling everything into a single bucket.
Related
- Wyrdlyre + HaulHard — Lew's dual-comp engagement is the canonical multi-org test case.
- Shared infrastructure — CF Access pattern, cloudflared tunnel.
- Vigil Steward — eventual integration point for time-based briefing summaries ("you logged 12 hours on FinishOps this week").