# Operator guide Day-to-day running of the pulsar service. ## Boot-time log markers Expected in `_app.logger` output after `services/pulsar/` initializes: ``` [pulsar] onWebInit — consumer + agent routes mounted [pulsar.bootstrap] indexes ensured [pulsar.bootstrap] preset template seeded: pulsar/web-fetch (static URL) [pulsar] serviceInit — ok ``` No per-boot secret generation step — the HMAC token layer was removed in M6.8 (see `03-architecture.md §12` for the allowlist model). If you see any `agentTokenSecret` references in logs, the running build pre-dates M6.8 — redeploy. ## Collections All under `androidBridge.*` prefix (same db as android-bridge): | Collection | What's in it | TTL | |---|---|---| | `pulsarJobs` | one row per consumer request | `expiresAt` (24 h) | | `pulsarAgents` | one row per active agent session | `expiresAt` (24 h) | | `pulsarApprovedDevices` | one row per `{ownerUserId, bid}` approved pair | optional per-row expiry | | `pulsarPendingDevices` | one row per unknown BID awaiting owner approval | 30 min | | `pulsarEvents` | append-only audit trail | 7 days | | `pulsarConfig` | system rows (tunables; no secret here any more) | — | Operator queries: ```js // All jobs for a specific user, newest first: db.getCollection('androidBridge.pulsarJobs').find({ownerUserId: ObjectId('…')}).sort({createdAt: -1}).limit(50) // Currently-executing jobs (dispatched, deadline in the future): db.getCollection('androidBridge.pulsarJobs').find({state: 'dispatched', dispatchDeadlineAt: {$gt: new Date()}}) // Agents still alive: db.getCollection('androidBridge.pulsarAgents').find({lastSeenAt: {$gt: new Date(Date.now() - 60000)}}) ``` ## Usage readout (per user) Lifetime counters (back-compat): ```js db.getCollection('androidBridge.accountStats').findOne({_id: ObjectId('')}) // → { // _id, username, pulsarJobsTotal, pulsarBytesInTotal, // pulsarBytesOutTotal, pulsarInsecureJobsTotal, // pulsarPrivateIPJobsTotal, pulsarLastAt // } ``` These counters are additive and never decremented. For a billing period, snapshot at start + end, subtract. Daily rollup (M8.c — read via `GET /pulsar/api/usage?from=&to=`): ```js db.getCollection('androidBridge.pulsarUsageDaily').find({ userId: '', date: {$gte: '2026-10-01', $lte: '2026-10-31'} }).sort({date: 1}) // → [{userId, date, jobs, bytesIn, bytesOut, msDuration, callbacks}, ...] ``` Wallet charges (soney debits) live in `walletLedger` / `walletHotCharge` and are owned by `services/payments/` (M10). Pulsar does not touch them; a biller cron (M11) reads `pulsarUsageDaily` and emits charges via `payments.charge(userId, soney, {source: 'pulsar', scope: 'fetch-request', ...})`. ## Approving / removing devices The pulsar webapp's **Devices** tab is the operator surface. For scripted ops, the HTTP endpoints: ``` GET /pulsar/api/approved-devices — list owned approved POST /pulsar/api/approved-devices — upsert approval DELETE /pulsar/api/approved-devices/:bid — revoke GET /pulsar/api/pending-devices — list pending (waiting for approval) ``` Admin users bypass the owner filter; everyone else is scoped to BIDs they own (via `pulsarApprovedDevices.ownerUserId`). The agent view at `/pulsar/agent/ui` has NO query-string credentials any more — just open the URL. A fresh device auto-registers as pending on first `/session` call; approve from Devices tab to activate. ## Monitoring Look for: 1. **Agents without recent `lastSeenAt`** — the device went offline but the row hasn't TTL'd yet. Normal if transient. 2. **`pulsarJobs` with high `dispatchDeadlineAt` churn** — many agents claiming + failing. Could be: upstream slow, bad network, agents returning `agent-refused-override` because their capabilities are mis-configured. 3. **Growth of `pulsarInsecureJobsTotal`** — someone is using the TLS override heavily. Check the user's audit trail — `pulsarEvents` receives one row per claim/callback with the override flag. 4. **`pulsarPendingDevices` growth** — pending BIDs accumulate when operators forget to review the Devices tab. Each row TTLs at 30 min; sustained non-zero count means devices keep retrying without ever being approved. ## Backup Pulsar collections are prefixed `androidBridge.*` so android-bridge's own backup tooling picks them up automatically. No separate step needed. There is no longer a `pulsarConfig` secret row to worry about (the HMAC token was removed in M6.8); the allowlist in `pulsarApprovedDevices` is the only state that matters for authorization, and it is part of the standard backup set. ## Troubleshooting ### Consumer gets `503 no-active-agents` Check: `db.androidBridge.devices.countDocuments({'owners.userId': ObjectId(''), status: 'approved', lastSeenAt: {$gte: new Date(Date.now()-60000)}})`. If > 0, the pulsar preset isn't attached — go to android-bridge-manager and attach `pulsar/web-fetch`. ### Agent connects but never sees jobs Check: - The device has the pulsar preset attached and enabled. - The agent's `capabilities` match what the user is sending. A user firing `allowInsecureTLS: true` will not reach an agent whose `insecureTLS` capability is `ignore` or `refuse`. - The row is actually in `pulsarJobs` — not rejected by the ingress SSRF check. ### Agent returns `agent-refused-override` repeatedly The capability toggles in the agent UI are set to `refuse` for a flag the user keeps requesting. Either the user needs to drop the flag or the operator needs to flip the toggle to `accept` (and understand the risk). ### Fresh device never reaches "approved" state - Open the pulsar webapp **Devices** tab → **Pending** section. - If the BID is listed, click **Approve**. The next `/session` call from the device will succeed within seconds. - If the BID is NOT listed, the device never reached `/session`. Check that the device opened the right URL (`/pulsar/agent/ui` under this sgapps.io host, no stale query-string with token). ### Browser agent needs CORS/mixed-content bypass Operator browsers hit cross-origin + mixed-content restrictions that AB Desktop WebViews don't. Install guide covers the launch flags per browser × per OS, with "ignore certificate errors" variants for self-signed local dev. See the pulsar webapp **Install** tab, section C.