Skip to content

Cloud server edition (plain English)

For: founder’s family teammates
Status: private pilot candidate only (WING-193). This is not permission to go live for the public.
Sources: docs/technical/cloud-run-server-edition.md, docs/technical/cloud-finance-usage.md


The short version

Most people use WorkWingman as a Windows app on their own computer (desktop edition). That stays the default.

The cloud server edition is a separate, locked-down version that runs in Google’s cloud so a small invited group (YC reviewers and team testers) can try a few features in a browser—without installing the desktop app.

Think of it like this:

  • Desktop edition = your own kitchen. You control the stove, the fridge, and the recipes on your counter.
  • Cloud server edition = a private tasting room in a building the family rents. Only people with the right invitation get in. The menu is short on purpose. The doors, locks, and fire sprinklers are checked every time we open for service.

What runs in the cloud

The “restaurant” stack (one public front door)

Piece What it is in plain words
Web app (Angular SPA) The pages you see in the browser—the dining room menu and tables.
API (server) The kitchen that takes orders and enforces the rules. Browser and API share one origin (same front door), so the browser is not given a raw “master key.”
Cloud Run Google’s managed place where that kitchen container runs.
Load balancer The only public entrance. The kitchen is not meant to be reached by a secret side door URL.
Firestore A small cloud notebook for invitation check-in records and sessions—not a dump of résumés and full desktop data.
Secret Manager A locked vault for invitation hashes, session-signing material, and the paid AI provider key—not plaintext passwords in source code or images.

Who gets in (two invitation lines)

Two separate audiences, each with its own high-entropy access code:

  1. yc-reviewer — YC review line
  2. team-test — team test line

Those codes are entry tickets only, not “you are now an admin” badges. Successful entry creates a private workspace for that person. People who share one invitation code still do not share each other’s data—each successful redemption gets its own random tenant workspace (like each guest getting a private locker, even if they used the same event password to enter the building).

Sessions live at most seven days (configured SessionHours, max seven). Logging out / revoking the session deletes that private tenant workspace for the pilot’s stored bits.

What this pilot actually serves

Only a short allow-list of API routes is open. Everything else from the big desktop app answers “not available in private pilot” (hard lock—operators cannot flip a config switch to open the rest).

In practice the pilot covers things like:

  • Onboard / session / logout — check in, see who you are to the server, leave
  • Study guide (cloud text generation path via Anthropic for the pilot)
  • Job explorer query + preferences — sample/demo jobs only; saved filters stay in your workspace

Not in this pilot’s live surface: the rest of the desktop app’s data routes (they stay locked until every store is proven tenant-safe end to end).

What is deliberately not cooked for YC

For the YC audience, these stay excluded (shown as “Not included in this YC test,” not as a crash):

  • Music generation
  • AI image generation
  • Image enhancement / upscaling
  • Image-to-video

Excluded means: do not ask for that provider path; no dispatch; no cost estimate from that path. Desktop keeps its normal rules. Team-test is a different audience, but non–study-guide provider features stay locked in this first private pilot too.

Sample jobs only

Job explorer uses a read-only demo catalog. Every sample job is marked demo, has no application link, and never reads desktop repositories. The seeded sample workspace is immutable; entering a code forks a private workspace—no two users share mutable state.

What is stored (and what is not)

Pilot storage is tight on purpose:

  • Bounded job-filter preferences under the server-resolved workspace
  • No résumé, profile, job-history, or provider prompt/output documents in this pilot design
  • Provider results for the current path are returned directly and not kept as stored prompts/generated content
  • Finance/usage design (planned) keeps a content-free ledger: spend aggregates only—never prompts, résumés, API keys, or raw workspace IDs in that ledger

How updates ship safely

Shipping here is less like “push any change and hope” and more like opening night after a checklist.

Before anyone deploys

Release blockers named in the technical contract include:

  • Full security council review
  • Historical gitleaks gate finding resolved with evidence/allowlisting (not rewriting history away)
  • Semgrep verdict
  • Firestore integration test against an authorized disposable project
  • Image checked under read-only / non-root settings
  • Named GCP project, region, service account, secrets
  • Billing and required APIs enabled
  • Andrew’s explicit authorization for that exact target and time window

How the building is locked when open

  • Runtime runs as a non-root user inside the container
  • Service account is narrow: secret accessor on specific secrets, narrow Firestore access—no project owner/editor, no broad storage/deploy/package roles
  • Invitation material in the vault is SHA-256 hashes of codes, not the plaintext codes
  • Plaintext codes are created offline, distributed out of band, and must never land in source, image layers, runtime logs, or deployment command lines
  • Rotating the session-handle key intentionally ends all existing sessions (everyone re-checks in)
  • Rotating an invitation hash disables the old hash so old tickets stop working

The “one cook, one burner” rule (on purpose)

For this private pilot the service is pinned to:

  • Max instances = 1
  • Concurrency = 1

That is a security boundary, not a performance tip. Some rate-limit “buckets” live in process memory. Extra copies of the kitchen would each get their own buckets and quietly multiply how much abuse is allowed. Live verification fails closed unless both values are exactly one. Scaling out later needs a distributed limiter first.

Front door topology

  • External Application Load Balancer + serverless NEG is the public path
  • Cloud Run ingress is limited; default run.app URL disabled
  • A Cloud Run “allow unauthenticated” invoker binding exists so the load balancer can call the service—it does not mean “anyone can use the app without an invitation.” App-level invitation, session cookie, CSRF, and tenant checks remain the real guest list

After every deployment: prove the locks

Rollout fails unless a live boundary verifier passes (ops/verify-yc-cloud-run-boundary.ps1). That script checks the real Cloud Run service, backend, and NEG: instance/concurrency = 1, correct ingress, default URL off, expected invoker binding, right load-balancer shape, NEG aimed at this service.

Local validation (build/run container, health checks, CSRF rejection, pilot route locks, non-root UID, no secrets in image history) is part of the same “prove it” culture—not a substitute for the live boundary check after deploy.


What can go wrong — and the guardrails

Risk: wrong person gets in

Guardrails

  • High-entropy invitation codes; only hashes in the vault
  • Separate codes, secrets, rotation, expiry, attempt budgets, and audit labels per audience
  • Rate limit on entry attempts (five-token client-IP bucket; one token every 12 seconds)
  • Generic error messages (no “helpful” hints that teach attackers)
  • Browser session is an HttpOnly, Secure, SameSite=Strict host-only cookie + server-side session—not “paste an API key in the browser”
  • State-changing requests also need a CSRF token tied to the session
  • Session age capped; rotate session after entry and after optional Google bind

Risk: one guest sees another guest’s stuff

Guardrails

  • New random tenant workspace per redemption when configured that way
  • Server resolves workspace only from the cookie session—raw workspace/tenant IDs do not go out on the public API
  • Every repository call needs that server workspace context; missing context fails closed
  • Job preferences for tenant A cannot be read by tenant B
  • YC vs team audiences stay partitioned

Risk: someone tries to open the whole desktop product through the cloud URL

Guardrails

  • Only exact pilot method/path pairs are admitted
  • Legacy app-data routes return 503 feature_not_available_in_private_pilot
  • Config cannot unlock them
  • SPA shows the locked pilot scope directly—it does not load the full desktop shell and hope failures are quiet

Risk: run-away AI spend

Guardrails (pilot + planned finance contract)

  • Study-guide generation limited to three requests per authenticated tenant per minute
  • Per-tenant UTC-day reservation counter refuses work whose configured worst-case cost would cross MaximumDailySpendUsd (prompt not sent when refused)
  • Planned hard dispatch caps before provider calls: per workspace/day, per workspace/month, application-wide day and month
  • Crossing a cap blocks dispatch; clients cannot override
  • Auto-reload of prepaid spend is off initially (finance plan)
  • YC-excluded features contribute zero estimated/actual provider cost because no provider call happens
  • Private alerts to Pam and Andrew at 20%, 50%, 80%, and 100% of application budget (plus forecast breach), deduplicated—aggregate spend only, no user content
  • GCP billing alerts are labeled as infrastructure signals; they do not stop an AI call—the app caps do

Exact full application budget dollar amount: TODO(verify) (not stated as a fixed total in cloud-finance-usage.md).

Risk: secrets leak (keys, codes, résumés in logs)

Guardrails

  • Secrets never in image layers, source, deploy CLI args, or shell history
  • Codes: generate safely, hash locally, store hash only, deliver plaintext privately
  • Finance ledger design: zero prompts, profile/résumé facts, generated content, provider keys, OAuth tokens, request bodies, filenames, job titles, employer names
  • Canary acceptance tests expected for “no sensitive content in ledger/logs/alerts/exports”
  • Do not log IP next to career PII

Risk: finance dashboard peeping

Guardrails (planned; not live from the finance spec alone)

  • Finance view needs a server-verified, Google-bound finance role assigned only to Pam
  • Having a YC/team code, owning a workspace, matching an email domain, or a client-side claim is not enough
  • Andrew gets alerts but does not automatically get the finance dashboard unless separately assigned
  • Workspace IDs in the ledger are pseudonyms, not raw tenant IDs

Risk: “health” endpoints become a data hose

Guardrails

  • /healthz = liveness only
  • /readyz = HTTP pipeline ready
  • Neither reads user data nor checks paid/external providers
  • Pre-auth /capabilities is fixed and content-free; it reports server-locked and does not reveal audience

Risk: process-local limits get diluted by scaling

Guardrails

  • Live verifier always requires max instances 1
  • Concurrency 1 remains the safe default; the explicit Cloud Live Run demo mode permits concurrency 8 for synchronous GKE provisioning and polling, without permitting horizontal replicas
  • Capacity when authenticated-session partitions fill: new partitions denied with 503 and a 60-second recheck hint (not a promise of entry)
  • Store-validation failures keep spent permits and discourage hammering a sick dependency

Risk: Google sign-in quietly merges people

Guardrails

  • Optional Google sign-in may bind the existing workspace only after explicit confirmation
  • It does not silently create, merge, or pick another user’s workspace

What this is not (so nobody oversells it)

  • Not public production authorization
  • Not the full desktop product in the browser
  • Not multi-instance scale-out ready under current in-process limiters
  • Finance dashboard / dispatch-cap enforcement from the finance contract must not be presented as live until tenant, authentication, and role enforcement for that plan are implemented

One-line family takeaway

Cloud server edition = a private, invitation-only tasting of a few cloud features, with a short menu, private lockers per guest, spend brakes, and a “doors checked after every remodel” rule—while the everyday product remains the Windows desktop app.