# 99Pages Agentic OS — Portable
## Product Definition Report (PDR)

| Field | Value |
| --- | --- |
| Product | **99Pages Agentic OS — Portable** |
| Version | v0.3.0 (engineering build), targeting v1.0 GA |
| Document | PDR.md |
| Authors | NG TICK KEE (Dicky Ng), 99Pages |
| Status | DRAFT — engineering complete on Windows path; awaiting macOS / Linux live verification + Portal SKU wiring |
| Last updated | 2026-05-18 |
| Related | [PORTAL_INTEGRATION.md](./PORTAL_INTEGRATION.md), [ARCHITECTURE.md](./ARCHITECTURE.md), [README.md](./README.md) |

---

## 1. Product overview

**Portable** is a USB-stick edition of 99Pages Agentic OS. The customer
receives a physical USB drive containing two visible files (a launcher
executable + an encrypted vault). Plug it into **any Windows, macOS, or
Linux** machine, double-click the launcher for that OS, and the full
Agentic OS Core + 30 MCP modules + 17 expert skills boot from the stick.
Memory persists on the USB itself. Pull the stick → nothing remains on
the host PC. The agent is driven from a local CLI shell, a bundled PWA
WebApp (LAN-accessible), or a paired Telegram bot — same memory across
all three surfaces.

One USB, three operating systems, one shared encrypted payload.

### Tag line
*"Your AI super-secretary on a stick. Plug in anywhere — Windows, Mac,
Linux. Pull it out — nothing left behind."*

### Positioning vs other Agentic OS editions

| Edition | Distribution | Multi-device | Price | Best for |
| --- | --- | --- | --- | --- |
| **Core (PAYG)** | One-line installer → `~/.agentic-os/` | Yes (Workspace Seat) | From USD $10 top-up | Daily desktop user, own machine |
| **Portable** ← *this PDR* | Physical USB | No (single-device) | **USD $198** one-off (incl. $100 credits) | Mobile professional, BYO-machine, air-gapped offices, contractors hot-desking |
| Workforce gateway only | Cloud SaaS | N/A | Per-call from credits | Developers wiring API into their own apps |

---

## 2. Problem statement

Three customer pains the Core edition does not fully address:

1. **"I work on someone else's computer."** Lawyers in client offices,
   consultants on shared kiosks, sales staff on customer laptops. Cannot
   install software. Cannot leave traces. Need own AI on demand.
2. **"I move between Windows / Mac / Linux."** A user has a Mac at home,
   a Windows desktop at work, a Linux box at the lab. They want **one
   agent with one memory** that goes wherever they go. Multi-device cloud
   workspace (the Core path) requires every device to be online + paid;
   Portable solves the "intermittent + offline first" case.
3. **"I don't want to install anything."** Family users, older buyers,
   non-technical purchasers who bounce off `curl | bash`. Plug-and-play
   USB is the lowest-friction install in computing — universally
   understood.

The Portable product is the answer to all three with one SKU.

---

## 3. Scope

### 3.1 In scope (v1.0)

- ✅ Single USB stick, hardware-bound (specific physical drive only)
- ✅ Three host operating systems: Windows / macOS / Linux
- ✅ Five architecture binaries: `windows/amd64`, `darwin/{arm64,amd64}`, `linux/{amd64,arm64}`
- ✅ Tamper-resistant vault (AES-256-GCM, HKDF key derivation, USB
  hardware serial as anchor)
- ✅ Real Agentic OS Core CLI + 30 MCP modules + 17 DSL skills (the same
  modules shipped in the PAYG installer)
- ✅ Memory persistence on the USB itself (`AGENTIC_OS_HOME =
  <USB_ROOT>/agentic-os-home`) — pull stick, plug into another PC,
  yesterday's journal entries are still there
- ✅ Local PWA WebApp surface on `http://127.0.0.1:7799` plus LAN
  interface — drive from phone or any browser on the same network
- ✅ Telegram bot integration (optional, configured via
  `$AGENTIC_OS_HOME/config/telegram.token`)
- ✅ USD $198 one-off Stripe Checkout with USD $100 credit auto-loaded
  to a new account at payment success
- ✅ First-run account binding via short magic-link redemption flow
  (see [PORTAL_INTEGRATION.md](./PORTAL_INTEGRATION.md))

### 3.2 Out of scope (v1.0)

- ❌ Multi-device cloud Workspace Seats (use Core edition for that)
- ❌ Workspace coordination with the Agent2Agent relay (single-device
  product by design)
- ❌ macOS code-signing + notarization (v1.1 — user accepts first-run
  Gatekeeper bypass via Right-click → Open)
- ❌ Kernel-level file copy-block (filter driver) — v2.0 evaluation only
- ❌ User-replaceable product secret / BYOK signing (vault key is
  product-wide)
- ❌ Self-service replacement if USB lost or damaged (manual support
  process: ID-verified unbind, ship replacement at cost of shipping)

### 3.3 Explicitly EXCLUDED from the vault payload

The vault must contain only the runtime needed to use the product.
Per user direction:

- **Excluded:** SDK / SDK_CONTRACT / PDR / Quickstart docs, README /
  CHANGELOG / dev scripts, `*.go` source files, test files,
  `agentic-os-installer` (the product IS the bundle), `agentic-workspace`
  (cloud workspace; not for portable), `demo-*` apps, any internal
  developer material.
- **Included (embedded inside their host module binaries via `//go:embed`):**
  17 skill DSL files (inside `agentic-mcp-skills`), wiki pages
  (inside `agentic-mcp-wiki`), library-ui static assets (inside
  `agentic-mcp-library-ui`).

---

## 4. Pricing & packaging

### 4.1 Price

| Item | Amount |
| --- | --- |
| One-off purchase price | **USD $198.00** |
| Workforce credit loaded at payment | **USD $100.00** |
| Net cost of physical USB + binding + shipping | USD $98.00 (covers BOM, fulfilment, support) |
| Subscription | **NONE** |
| Auto-renewal | **NONE** |
| Refund | **NONE — credits non-refundable, USB non-returnable once opened.** Stated at Stripe Checkout. |

Customer payment of $198 → instantly:
1. Stripe webhook → Portal API
2. Account auto-created if email is new (magic-link, no password)
3. `gap_accounts.balance_usd += 100.00` deducted only on actual Workforce calls
4. Single-use 24-char base32 redemption code stored + emailed
5. Physical USB shipping queued

### 4.2 BOM (Bill of Materials) — internal

| Component | Cost (USD) |
| --- | --- |
| SanDisk-class USB 3.0, 8–16 GB, branded sleeve | ~$3 – $6 |
| Printed insert card (instructions + URL) | ~$0.50 |
| Worldwide tracked shipping (avg) | ~$8 – $25 |
| Fulfilment labour | ~$2 |
| Portable engineering amortisation (per unit @ 500 unit run) | ~$15 |
| Workforce credit COGS (avg 80% of $100 face value redeemed → ~$25 upstream model cost) | ~$25 |
| **Total COGS estimate** | **~$54 – $73** |
| Gross margin per unit | **~63 – 73%** |

### 4.3 SKU code

`agentic-os-portable` (lowercase, hyphenated). Stripe Product metadata
must carry `sku: agentic-os-portable` so the webhook handler can branch.

### 4.4 Sales channels

- **Primary (v1.0):** Direct from `https://99pages.uk/portable` →
  Stripe Payment Link → magic-link account creation + shipping form.
- **Secondary (v1.1+):** Bulk SKU for resellers (10-pack @ ~$1,750
  trade price, terms TBD).
- **Out of scope v1.0:** Marketplace listings (Amazon, Lazada),
  enterprise procurement, channel partners.

---

## 5. Functional requirements

### 5.1 First-run experience

| ID | Requirement |
| --- | --- |
| F-1 | User plugs USB into any Win / Mac / Linux PC. |
| F-2 | User double-clicks the matching launcher (`.exe` / `.command` / `.sh`). |
| F-3 | Launcher reads USB hardware + volume serials, derives AES key, decrypts vault, extracts to ephemeral temp dir. **Time budget: ≤ 30s on USB 2.0, ≤ 10s on USB 3.0.** |
| F-4 | First-run only: launcher opens default browser to `https://app.99pages.uk/portable/redeem?usb=<hw>` and waits for the user to paste the redemption code from their email. Portal binds USB ↔ account, returns signed JWT, written to `bind.json` on the USB. |
| F-5 | Subsequent runs: launcher detects `bind.json` is valid, skips browser, boots straight into `portable-core`. |
| F-6 | `portable-core` prints banner with PWA local URL + LAN IP + Telegram registration hint, then idles. |
| F-7 | User unplugs USB → all child processes die within 3 seconds (handled by `portable-core`'s context). |

### 5.2 Running agent surfaces

| Surface | Requirement |
| --- | --- |
| Local CLI | `portable-core` exposes interactive CLI shell when launched without `-pwa-only` / `-telegram-only` flags. (v1.1 deferral acceptable — v1.0 may launch PWA only.) |
| PWA WebApp | Bound to `0.0.0.0:7799` on launch. LAN-reachable. Installable to home screen (manifest + service worker). |
| Telegram bot | Only if `$AGENTIC_OS_HOME/config/telegram.token` exists (user-configured). Uses the user's own BotFather token. Restricted to chats matching `bind.json.allowed_chat_ids`. |

### 5.3 State persistence

| ID | Requirement |
| --- | --- |
| F-8 | `AGENTIC_OS_HOME = <USB_ROOT>/agentic-os-home`. **No host PC paths.** |
| F-9 | Every module's SQLite (`journal`, `library`, `keybox`, `calendar`, `project`, `people`, `briefingstore`, `rss`, etc.) writes inside `AGENTIC_OS_HOME`. |
| F-10 | Move the USB to another PC: all journal entries, library docs, calendar events, contacts visible. |
| F-11 | Launcher temp extraction at `%TEMP%/$TMPDIR` is best-effort deleted on normal exit. On power-pull it leaks (acceptable; contents are useless without the running agent and the user's account binding). |

### 5.4 Network requirements

| ID | Requirement |
| --- | --- |
| F-12 | All LLM / vision / voice / TTS / image calls go via the Workforce gateway (no BYOK in Portable). Internet required for these calls. |
| F-13 | Air-gapped fallback: local-only modules (journal, library, calendar, project, people, workflow, fs, ssh, keybox, harness) work without network. |
| F-14 | Workforce gateway charges against the bound account's `balance_usd`. When balance < $1, calls fail with a user-actionable message + link to top up. |

### 5.5 Copy-resistance

| ID | Requirement |
| --- | --- |
| F-15 | Copying `99pages.vault` to a different USB → key derivation fails → user-facing error explains re-provisioning. |
| F-16 | Copying `99pages-portable.exe` (or other-OS equivalents) alone → no usable bytes (the launcher is small + useless without the vault). |
| F-17 | Reformatting the USB changes the volume serial → vault is dead → user must re-provision. |
| F-18 | Honest scope: an attacker with sufficient skill can RAM-dump extracted binaries while the launcher runs. **This is acceptable** — the Workforce gateway enforces account + balance for every call, so pirated binaries return errors. |

---

## 6. Non-functional requirements

### 6.1 Performance

| Metric | Target | Notes |
| --- | --- | --- |
| Cold start (USB 2.0) | ≤ 30s | Vault decrypt + tar extract dominates |
| Cold start (USB 3.0+) | ≤ 10s | |
| PWA first byte | ≤ 1s after `portable-core` boot | |
| Per-module spawn | ≤ 500ms | Modules are small Go binaries |
| Memory at idle | ≤ 400 MB (portable-core + webapp child) | |

### 6.2 Security

| Requirement | Spec |
| --- | --- |
| Vault confidentiality | AES-256-GCM with random 12-byte nonce + 16-byte HKDF salt per vault |
| Vault integrity | GCM auth tag on both manifest and body |
| Key binding | USB hw serial (normalised cross-OS) + USB vol serial + product secret + per-vault salt → HKDF-SHA256 → AES-256 key |
| Product secret rotation | New secret invalidates only **future** vaults; deployed USBs continue to work |
| Path-traversal defence | Tar extractor refuses any entry whose cleaned path is absolute or starts with `..` |
| Code-signing | Windows: Authenticode (v1.1, post-revenue) · macOS: Developer ID + notarisation (v1.1) · Linux: none |
| Network | Workforce gateway over TLS 1.3, certificate pinning optional |

### 6.3 Reliability

| Requirement | Spec |
| --- | --- |
| USB device lifetime | SLC/MLC industrial-grade flash recommended (3-year mainstream warranty) |
| `portable-core` child supervision | Restart child on crash with exponential backoff (v1.1; v1.0 = log + exit) |
| Idempotent first-run binding | Replaying `POST /api/v1/portable/bind` with same params returns existing JWT (200, not 409) |

### 6.4 Compatibility matrix

| OS | Arch | Status |
| --- | --- | --- |
| Windows 10/11 | amd64 | ✅ v0.3 live-verified |
| Windows 10/11 | arm64 | ⏳ v1.1 |
| macOS 12+ (Apple Silicon) | arm64 | ⏳ code complete, needs hardware verification |
| macOS 12+ (Intel) | amd64 | ⏳ code complete, needs hardware verification |
| Linux (glibc 2.31+) | amd64 | ⏳ code complete, needs hardware verification |
| Linux | arm64 (Raspberry Pi 4+) | ⏳ code complete, needs hardware verification |
| ChromeOS Crostini | amd64 | ⏳ best-effort, untested |

---

## 7. Architecture summary

(Full detail: [ARCHITECTURE.md](./ARCHITECTURE.md).)

```
USB root
├── 99pages-portable.exe              Windows launcher
├── 99pages-portable.command          macOS launcher (Terminal wrapper)
├── 99pages-portable.sh               Linux launcher (shell wrapper)
├── 99pages.vault                     ≈ 450 MB AES-256-GCM container
├── START_HERE.txt
├── bin/
│   ├── 99pages-portable.mac-arm64
│   ├── 99pages-portable.mac-x64
│   ├── 99pages-portable.linux-amd64
│   └── 99pages-portable.linux-arm64
└── agentic-os-home/                  Created on first run; carries state
    ├── journal.db
    ├── library.db
    ├── config/telegram.token         (optional, user-created)
    └── bind.json                     (signed device JWT from Portal)

Inside 99pages.vault (decrypted):
bin/
├── windows-amd64/                    31 binaries: portable-core + 30 modules
├── darwin-arm64/                     31 binaries
├── darwin-amd64/                     31 binaries
├── linux-amd64/                      31 binaries
├── linux-arm64/                      31 binaries
└── registry.json
```

Key derivation: `K = HKDF-SHA256(normalise(hw_serial) || "|" ||
normalise(vol_serial), random_salt, product_secret || "|99pages.portable.v1")`

Per-OS serial normalisation lives in `internal/usbid/normalize.go` so
Windows / macOS / Linux derive the **same** key from the same physical
USB despite each OS reporting the serial with different padding /
framing.

Manifest `entry_by_os["<goos>/<goarch>"]` map → launcher picks the right
`portable-core` binary out of the extracted `bin/<os>-<arch>/` tree.

---

## 8. User journey

### 8.1 Purchase

1. User reads marketing at `https://99pages.uk/portable`.
2. Clicks "Buy — USD $198" → Stripe Payment Link.
3. Enters email + shipping address. Pays.
4. Receives within 2 emails:
   - **Order confirmation** with Workforce account magic-link.
   - **Shipping notification** with tracking number + estimated arrival.
5. Account is already live with USD $100 balance — user can sign in to
   `app.99pages.uk` immediately to see balance, even before USB arrives.

### 8.2 First plug-in

1. USB arrives. User opens insert card → URL `99pages.uk/start`.
2. Plugs USB. Double-clicks matching launcher for their OS.
3. Launcher takes ~10–30s to extract. Browser pops open to
   `app.99pages.uk/portable/redeem`.
4. User pastes 24-char code from email. Portal binds + returns
   `bind.json`. Browser shows: "All set — return to the launcher."
5. `portable-core` boots. Banner shows local + LAN PWA URL.
6. User opens `http://<LAN-IP>:7799` on their phone. Chat works.

### 8.3 Daily use

- Plug stick in → double-click launcher → start chatting / asking /
  giving voice commands.
- Telegram bot (if configured) keeps working even when stick is
  unplugged? **No.** Telegram polling lives in the spawned
  `agentic-mcp-telegram-bot` process; unplug = die. This is by design —
  the stick is the device of authority.

### 8.4 Cross-machine continuity

- Pull stick from Mac at home → plug into Windows at office → all
  yesterday's journal entries, library docs, conversations, todos still
  there. Same login. Same key. Different OS.

### 8.5 Loss / theft

- User signs in to `app.99pages.uk` from any machine.
- Account → Devices → Portable USBs → click "Unbind".
- Bound JWT invalidated server-side. Even if attacker has the stick,
  Workforce calls will 401.
- Replacement USB: support flow, ID verification, customer pays
  shipping.

---

## 9. Distribution & fulfilment

### 9.1 Manufacturing

- **v1.0 (first 100 units):** Manually flashed from a master image by
  NG TICK KEE in Kuala Lumpur. Each unit is provisioned per its own
  USB serial — the flash step calls `deploy-to-d -modules-repo ...
  -target <usb-root>` with that particular USB plugged in.
- **v1.1 (100–1000 units):** Outsource flash + labelling to a Malaysian
  USB fulfilment partner (TBD). Master image cannot be pre-flashed
  because each vault binds to a specific USB hardware serial — partner
  needs the build pipeline.
- **v2.0:** Re-evaluate. Options: ship blank USBs with a separate
  "Activate" tool that takes a one-time-use voucher; or accept per-unit
  flash cost.

### 9.2 Shipping

- Worldwide tracked shipping via Pos Malaysia / DHL eCommerce / similar.
- 5–15 business days typical.
- Customer pays no extra shipping inside Malaysia; international
  shipping included in $198 ex-MYS but adjustments for remote zones
  TBD.

### 9.3 Support

- Email: `dicky@pbcap.org`
- Knowledge base: `app.99pages.uk` → Help section (TBD)
- First-line issues: vault won't decrypt (90% of which are users with
  the wrong USB plugged in); launcher won't run (Gatekeeper /
  SmartScreen first-run prompts).

---

## 10. Success metrics

### 10.1 Launch metrics (first 90 days)

| KPI | Target |
| --- | --- |
| Units sold | 50 |
| Refund / chargeback rate | < 2% |
| First-week activation rate (USB bound to account) | > 80% |
| 30-day return-to-purchase (top-up after credits run out) | > 30% |
| Support tickets per unit | < 0.4 |

### 10.2 Steady-state (year 1)

| KPI | Target |
| --- | --- |
| Units / month | 30+ |
| Average revenue per Portable user (incl. top-ups) | $260 / 12 months |
| Gross margin | ≥ 65% |
| NPS | ≥ 50 |

---

## 11. Roadmap

| Version | Scope | ETA |
| --- | --- | --- |
| v0.3.0 | Cross-platform launcher + cross-compile pipeline + real Core + 30 modules in vault. **Engineering complete on Windows.** | ✅ 2026-05-18 |
| v0.4.0 | macOS + Linux live-verified. First-run browser-redeem flow + `bind.json`. | 2026-05-25 |
| v0.5.0 | Portal `agentic-os-portable` SKU live with $198 Stripe Checkout + $100 auto-credit + binding endpoints. | 2026-06-01 |
| v1.0 GA | First 100 USB units flashed + shipped. `99pages.uk/portable` landing page live. | 2026-06-15 |
| v1.1 | Windows Authenticode + macOS notarisation. Telegram persistence across launcher restarts. Bulk SKU. | 2026-07 |
| v2.0 | Hardware dongle option (separate Enterprise SKU at $498 with crypto chip — defeats RAM-dump). | 2026-Q4 |

---

## 12. Risks & open questions

| # | Risk | Mitigation |
| --- | --- | --- |
| R-1 | macOS Gatekeeper blocks unsigned `.command` on first run | Document Right-click → Open workflow in insert card. Add notarisation in v1.1 once $198×500 = $99k revenue justifies $99 / year Apple Developer cost. |
| R-2 | Linux distros without `udevadm` available | `lsblk` fallback already in `usbid_linux.go`. Test on minimal Alpine / busybox-init boxes. |
| R-3 | USB hardware serial collision (some cheap clones report all-zeros) | Reject USB at `deploy-to-d` time if `NormalizeHW(hw) == "0"`. Document supported brands. |
| R-4 | exFAT corruption on hot-pull during write to `agentic-os-home` | Use SQLite WAL with synchronous=NORMAL, journal_mode=WAL. Documented limitation. |
| R-5 | Customer's USB is lost / stolen | Self-service unbind via Console; replacement SKU at shipping cost. |
| R-6 | Per-unit manual flashing doesn't scale beyond ~200 units / month | Workflow with fulfilment partner in v1.1. |
| R-7 | Product secret leak from launcher binary reverse-engineering | Accepted: product secret is one of three factors. Without the matching USB serial + the user's account binding, leaked secret alone is inert. Rotate in v2.0 with new launcher. |

### Open questions

- Q-1: Should Portable users get **multi-USB discount** (e.g. own one
  for home, one for office)? Not in v1.0.
- Q-2: Should there be a **transferability** flow (gift / resell a
  Portable USB)? Not in v1.0 (account-bound).
- Q-3: Branded sleeve / packaging design — outsource to
  designer or in-house Vibe Coding? TBD.

---

## 13. Approval

| Role | Name | Sign-off |
| --- | --- | --- |
| Product owner | NG TICK KEE | — |
| Engineering | NG TICK KEE / Claude | v0.3.0 ✅ |
| Operations / fulfilment | NG TICK KEE | — |

---

*© 2026 99Pages. Confidential and proprietary. Distribution restricted to
internal team and authorised partners.*
