Skip to content

Friends trial (multi-tenant Phases 2–4): guest mode + hosting + cost caps - #15

Merged
ssethia2 merged 3 commits into
mainfrom
ss/multi-tenant-phase2
Jun 16, 2026
Merged

Friends trial (multi-tenant Phases 2–4): guest mode + hosting + cost caps#15
ssethia2 merged 3 commits into
mainfrom
ss/multi-tenant-phase2

Conversation

@ssethia2

Copy link
Copy Markdown
Owner

"Send a friend a link." Builds on the Phase 1 tenant core (#13) to make the web voice client multi-user and invite-only.

What's here

  • server.py serves the guest agent (account-free toolset), not the owner agent. Both /live-token and /agent are invite-gated by a per-friend token (friends.json: token → user_id) — unknown token → 403, so strangers can't run up your Gemini/Claude bill. /agent sets the tenant context for the request → isolated per-user memory + guest mode + a per-friend thread (guest-<uid>), always reset in a finally.
  • Guest Live prompt advertises only memory/web/weather (no email/calendar), so the front model sets the right expectations.
  • index.html reads the invite token from ?t= and passes it through; clean "invalid invite" message.
  • add_friend.py generates a token + prints the invite link (reuses a friend's token so their memory persists). friends.json gitignored; friends.example.json shipped.

Safety

  • Invite-only; each friend isolated (own memory collection, own thread, restricted tools); owner data never exposed.
  • 4 new tests prove the gating + that a valid token routes into an isolated guest context (right user, guest-<uid> thread, context cleaned up after). 216 pass.

After this: Phase 3 — host on a ~$5 VPS / the Pi with a stable HTTPS domain. Phase 4 — per-friend cost caps.

🤖 Generated with Claude Code

ssethia2 added 2 commits June 16, 2026 00:00
Turns the web voice client into "send a friend a link". Builds on the Phase 1 tenant core.

- server.py: serves the GUEST agent (account-free toolset) instead of the owner agent.
  Both /live-token and /agent are invite-gated by a per-friend token (friends.json:
  token → user_id); unknown token → 403, so strangers can't run up your Gemini/Claude bill.
  /agent sets the tenant context for the request → isolated per-user memory + guest mode +
  a per-friend thread (guest-<uid>); always reset in a finally.
- Guest Live prompt: only memory/web/weather (no email/calendar), so the front model sets
  the right expectations.
- index.html: reads the invite token from ?t=, passes it to /live-token and /agent; clean
  "invalid invite" message.
- add_friend.py: generates a token + prints the invite link (reuses a friend's token so
  their memory persists). friends.json gitignored; friends.example.json shipped.
- 4 tests (auth gating + a valid token routing into an isolated guest context); 216 pass.

Next: Phase 3 (host it on a $5 VPS / Pi with a stable HTTPS domain) and Phase 4 (cost caps).

Assisted-by: Claude Code (Opus 4.8)
Deploy artifacts so friends can use their invite links 24/7 without the laptop — on a
~$5 VPS or the Pi. Mirrors the pi/ {{USER}}/{{APP_DIR}} systemd convention.

- aria-webvoice.service: uvicorn on 127.0.0.1:8800 (not publicly exposed itself).
- aria-webvoice-tunnel.service + tunnel.sh: stable HTTPS via an ngrok reserved domain
  (ARIA_NGROK_DOMAIN), so the home-screen links keep working.
- install.sh: seds the placeholders, installs + enables both units.
- HOSTING.md: the full one-time setup recipe + notes (localhost-bind + tunnel keeps it
  off the open internet; no proactivity engine here so it's safe alongside the Telegram
  bot; cost caps are Phase 4).

Assisted-by: Claude Code (Opus 4.8)
@ssethia2 ssethia2 changed the title Multi-tenant Phase 2: webvoice multi-user guest mode (invite links) Multi-tenant Phase 2+3: webvoice guest mode + always-on hosting Jun 16, 2026
@ssethia2

Copy link
Copy Markdown
Owner Author

Added Phase 3 (hosting) to this PR — webvoice/deploy/ has systemd units (uvicorn bound to localhost + an ngrok stable-domain tunnel), an install.sh, and HOSTING.md. So this PR now covers the whole friends-trial except Phase 4 (per-friend cost caps). Deploy on a $5 VPS or the Pi → friends use their links 24/7.

Safety rail so one guest can't run up the Gemini/Claude bill — the last piece before
the trial is safe to hand to friends unattended.

- usage.py: JSON-backed daily counters (webvoice/usage.json, gitignored), per user+kind,
  env-tunable: ARIA_GUEST_DAILY_TOKENS (Live sessions/day, default 12) and
  ARIA_GUEST_DAILY_AGENT (brain calls/day, default 60). Keeps only today's bucket.
- server.py: /live-token over cap → 429; /agent over cap → a friendly "try tomorrow"
  result (the voice speaks it). Gated per resolved user_id.
- index.html: shows the server's cap/invite message on any non-OK token fetch.
- .env.example + HOSTING.md: document the cap knobs + ARIA_NGROK_DOMAIN.
- 4 tests (cap blocks after limit, per-user/per-kind budgets, endpoint 429/message); 220 pass.

Completes the friends-trial: Phase 1 isolation + 2 guest mode + 3 hosting + 4 caps.

Assisted-by: Claude Code (Opus 4.8)
@ssethia2 ssethia2 changed the title Multi-tenant Phase 2+3: webvoice guest mode + always-on hosting Friends trial (multi-tenant Phases 2–4): guest mode + hosting + cost caps Jun 16, 2026
@ssethia2

Copy link
Copy Markdown
Owner Author

Added Phase 4 (per-friend daily cost caps) — usage.py + gates on /live-token (429) and /agent (friendly message), tunable via ARIA_GUEST_DAILY_TOKENS/ARIA_GUEST_DAILY_AGENT. This PR now covers the whole friends trial: isolation + guest mode + hosting + caps. 220 tests pass.

@ssethia2
ssethia2 merged commit b167af6 into main Jun 16, 2026
1 check passed
@ssethia2
ssethia2 deleted the ss/multi-tenant-phase2 branch June 16, 2026 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant