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-minute debits)
  ├── Storage (one EBS volume, per-GB-month 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:

  1. Click Pair in the portal — we generate a four-digit PIN.
  2. Type the PIN in Moonlight on the device you want to play on.
  3. 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-minute debit while the session is in running. The rate depends on the machine tier you built (from Rp 43,000/hr on the entry tier, 720p streaming included) and is published on the credits page.
  • Storage — per-minute debit for your EBS volume at Rp 2,000/GB-month, on the full volume size. There is no free tier.

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 is billed at Rp 2,000/GB-month on the full volume size (metered per minute) — there is no free tier. You choose the size (200 GB – 1 TB) when you build your machine; you can grow it later (AWS only grows, never shrinks) and the partition extends on your next session start.

You can also snapshot your disk as a save-point at any time (while you have a computer and credit). Snapshots are held at the cheaper Rp 1,000/GB-month rate, billed continuously from creation on their actual stored size, and you can restore or rebuild from one.

If you delete your account, the volume goes too — account deletion is immediate, not deferred.

Grace period & data retention

When your wallet hits zero, Pawpado does not delete anything immediately. Billing pauses, the instance stops, and your storage enters a grace period with two windows:

  1. Live window. Your instance is kept stopped-but-ready, exactly as you left it. A top-up resumes it instantly — no restore step, no reinstall. The live window is 1 day.
  2. Snapshot window. After the live window ends, the root disk is snapshotted and the live volume is released (a snapshot is cheaper to hold than an attached volume). A top-up restores the instance from that snapshot — same library, saves, and mods — on your next start.
  3. Deletion. Only if no top-up arrives by the end of the snapshot window is the data permanently deleted.

How long the snapshot window lasts scales with your lifetime top-ups — the more you've spent on Pawpado, the longer we hold your data:

Tier Lifetime top-ups Live window Snapshot window
Loyal ≥ Rp 500,000 1 day 7 days
New < Rp 500,000 1 day 3 days

The distinction between the two windows is how fast you come back online: in the live window a top-up gives you an instant resume; in the snapshot window the instance is restored from backup on the next start (a short one-time restore). Either way your data is intact.

A single top-up of any amount lifts you out of grace, restores everything, and resets the grace clock — if you later run dry again, you get a fresh full live + snapshot window.

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:

  1. Mints an ephemeral Tailscale auth key scoped to your tag (tag:pawpado-user-<huudisUserId>).
  2. Registers the EC2 instance with that key on boot.
  3. 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 — the size of your EBS volume in GB (200–1000), chosen when you build your machine. Can be grown later (never shrunk).
  • region — AWS region. Defaults to ap-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 === true before pairing. Don't trust state === 'running' alone.
  • Idempotent start/stop. Calling start while already running returns the existing session, not an error. Calling stop while 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_CREDITS if you call start empty.

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