Concepts
This page covers the building blocks of Pawpado: what each object is, how they relate, and the lifecycle they go through. Read this before you go deep on any specific feature — the rest of the docs assume you know what a session is and how it differs from a pair.
The model
Account (one Huudis identity)
├── Credits (one wallet, per-second debits)
├── Storage (one EBS volume, per-GB-hour debits)
├── Session (zero or one at a time)
│ ├── Tailscale registration
│ └── Pairing (zero or many over the session's life)
└── Settings (auto-stop, region, preferred hours)
Pawpado is single-instance per user. You can have at most one running session at a time. Trying to start a second while one is running returns an error — the API expects you to stop the first one explicitly.
Account
An account is the Pawpado record attached to your Huudis identity. There's no separate Pawpado password — everything ties back to your Huudis user ID (huudisUserId).
You don't create an account explicitly. It exists the first time you sign in. The dashboard's top-right menu shows the email Huudis returned, and Settings → Account → Delete can purge it (including the EBS volume) if you want to leave.
Session
A session is one streaming desktop — one EC2 instance, one Tailscale registration, one period of compute-meter activity. It progresses through a small state machine:
| State | Means |
|---|---|
idle |
Nothing running. Storage may still exist. |
provisioning |
EBS volume is being attached, control plane is preparing the launch. |
starting |
EC2 instance is booting. Pawpado is polling Tailscale + Apollo for readiness. |
running |
Instance is up. Not yet pairable — wait for ready. |
stopping |
You hit Stop. Compute meter has already paused. |
stopped |
Terminal state. Volume detached, ready for a future session. |
failed |
Something went wrong. The error is surfaced on the dashboard. |
SessionStatus also carries a separate boolean: ready. It flips to true only after the Tailscale registration is confirmed and the Apollo streaming server has registered with the control plane. The portal disables the Pair button until ready is true.
Sessions don't have IDs you'll see — there's only ever one at a time per user, so the API exposes singletons (/api/v1/sessions/start, /api/v1/sessions/poll, etc.) rather than typed IDs.
Pairing
A pair authorizes one Moonlight client to talk to the Apollo streaming server inside your session. Pairing is per-device:
- Click Pair in the portal — we generate a four-digit PIN.
- Type the PIN in Moonlight on the device you want to play on.
- Apollo + Moonlight handshake; the device is added to Apollo's known-clients list.
A pair lives for the lifetime of the session. When the session stops, the Apollo allowlist is rebuilt from scratch on the next start — so you'll pair again next time.
The PIN expires in two minutes. If you took too long, request a new one.
Why a fresh pair each session? The Apollo allowlist is part of the EBS volume's state. We deliberately wipe the streaming-server config on session start so a leaked device pairing from yesterday can't connect today. Tailscale tag scoping enforces it even if pairing leaked.
Credits
Credits are your prepaid balance, denominated in IDR (or USD, depending on how you topped up). One wallet per account. Two meters draw from it:
- Compute — per-second debit while the session is in
running. The rate is a fixed per-second rate published on the credits page, with small fluctuations following AWS spot pricing. - Storage — per-GB-hour debit on the size of your EBS volume above the free tier. The free tier is set on Settings → Storage.
Credits are managed through Plugipay. Top-ups are checkout sessions: you redirect to Plugipay's hosted page, pay with any method (card, virtual account, e-wallet, QRIS), and Pawpado credits the balance on webhook delivery.
There are no refunds for compute already burned. If you want to leave, you can withdraw unused credit back to your original payment method — see Settings → Credits → Withdraw.
Storage
Storage is one EBS volume per user, attached to the session while it's running and detached (but kept) while it's stopped. Your installed games (a 100 GB Steam library, say) persist between sessions on this volume.
Storage has two prices:
- The first N gigabytes are free (set on the storage settings page).
- Everything above N is billed at a per-GB-hour rate.
The volume's size auto-grows as you install more. You can also cap it manually if you want to enforce a budget.
If you delete your account, the volume goes too — with a 30-day grace period so accidental deletes are recoverable.
Tailscale connection
Pawpado uses Tailscale to route Moonlight traffic from your device to the GPU instance, peer-to-peer where possible. Every account is a member of the Forjio tailnet (taile685d6.ts.net) under a per-user tag scope.
Per session, Pawpado:
- Mints an ephemeral Tailscale auth key scoped to your tag (
tag:pawpado-user-<huudisUserId>). - Registers the EC2 instance with that key on boot.
- Lets the Apollo streaming server bind to the tailnet IP only — never the public IP.
The ephemeral key expires after 24 hours. When the session stops, the node is removed from the tailnet within a minute.
You'll see the tailnet IP in the API response (tailnetIp), but you don't need to use it manually — Moonlight discovers the instance once you've paired.
Settings
Settings are per-account preferences. The current set:
autoStopMinutes— idle-input timeout. Default 15 minutes. Pawpado polls Apollo for last-input timestamp and stops the session when this is exceeded.storageGb— cap on EBS volume growth. Defaults to no cap.region— AWS region. Defaults toap-southeast-3(Jakarta). Other regions are roadmap.preferredHours— not yet enforced; for future scheduled launches.
You change settings via Settings → Preferences in the portal or PATCH /api/v1/settings on the API.
Identifiers
Most Pawpado objects don't expose IDs to you because there's only one of each per account. The exceptions:
| Prefix | Type | Where you'll see it |
|---|---|---|
i-xxxxxxxx |
EC2 instance ID | SessionStatus.instanceId |
vol-xxxxxxxx |
EBS volume ID | Admin endpoints, audit logs |
cs_… |
Plugipay checkout session ID | When you top up credits |
Your Huudis user ID (huudisUserId) is the durable account identifier, but you don't typically interact with it directly — the bearer token carries it.
What this means in practice
The model isn't ornamental — it shapes how integrations work:
- Polling for readiness. Always wait for
ready === truebefore pairing. Don't truststate === 'running'alone. - Idempotent start/stop. Calling
startwhile already running returns the existing session, not an error. Callingstopwhile already stopped is a no-op. - Credit checks. The portal disables Start session when your balance is below ~15 minutes of compute. The API also rejects with
INSUFFICIENT_CREDITSif you callstartempty.
Once you have this mental model, the rest of the docs are linear: each portal page, each API endpoint, each SDK method maps cleanly to one of these objects.
Next
- Auth overview — how Huudis tokens authenticate every call.
- Portal tour — the dashboard, page by page.
- API overview — every endpoint.