Protocol and security
How Polaris and Pulsar authenticate every message, and what each side refuses.
All calls go to {POLARIS_URL}/api/pulsar/v1/… over HTTPS only. Plain HTTP needs POLARIS_PULSAR_ALLOW_HTTP=true on Polaris and PULSAR_ALLOW_INSECURE=true on the satellite — lab use only.
Signatures
Both directions are signed with the satellite’s 64-character secret:
Request X-Pulsar-Id, X-Pulsar-Instance, X-Pulsar-Timestamp (±300 s), X-Pulsar-Nonce (single use)
X-Pulsar-Signature = hex(HMAC-SHA256(secret, ts \n nonce \n METHOD \n /api/path \n sha256(body)))
Response X-Polaris-Timestamp
X-Polaris-Signature = hex(HMAC-SHA256(secret, "polaris" \n ts \n request-nonce \n status \n sha256(body)))
Websocket {"body": "<json>", "ts", "id",
"sig": hex(HMAC-SHA256(secret, "polaris-ws" \n event \n ts \n id \n body))}
- Pulsar → Polaris: signed, timestamped, nonces are single use, the credentials are bound to the first instance that registered.
- Polaris → Pulsar: the satellite acts only on responses and websocket commands whose signature matches, that are at most 5 minutes old and (websocket) whose id it has not seen before. A man in the middle can neither inject nor replay plans, jobs or console grants.
- Shared test vectors in both test suites prove both sides compute identical signatures.
Endpoints
| Endpoint | Purpose |
|---|---|
POST register | first contact binds the instance; returns portal info, intervals, websocket settings, abuse contact |
POST heartbeat | round-trip times, connectivity, websocket state, queue size, load, active runs, geo |
GET sync | plans with schedules, options and resolved targets, plus a version hash |
POST progress | live parts of a running plan or job |
POST results | batch of finished runs; answers the accepted run ids |
POST jobs/{uuid}/status, jobs/{uuid}/result | immediate jobs |
POST logs, POST trace | log shipping, traceroute to the portal |
POST ws/auth | signature for the private channel private-pulsar.{uuid} |
Websocket events on private-pulsar.{uuid}: job.run, plans.updated, console.update.
What Polaris accepts
Every upload is rebuilt by a strict validator (app/Support/PulsarPayload.php):
- only plans assigned to that satellite, only targets of that plan or job,
- nmap hosts only inside the scanned address, network or DNS answers,
- typed and bounded fields; unknown keys are dropped, control characters stripped,
- domain checks only on domain targets, host checks never on them.
Rejected results are written to the satellite log.
Defence in depth on the satellite
Targets and options are validated again before they reach nmap, traceroute or ping (pulsar/validate.py): only addresses, networks, ranges and hostnames in strict formats, ports and numbers in their ranges. No value can become a command-line option.