Documentation

Security

Hiras runs a server on your phone, so its security model matters. The design goal is simple: the gateway is reachable only from your own network, only with your key, and your data never leaves the device. Here’s exactly how that’s enforced.

The local-network guard

Before anything else — before the API key is even checked — Hiras inspects the caller’s address. Only private ranges are allowed through:

  • Loopback127.0.0.0/8
  • RFC 191810.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
  • RFC 6598 (carrier-grade NAT / VPN overlays) — 100.64.0.0/10
  • Link-local169.254.0.0/16

Any other address — anything from the public internet — is rejected with HTTP 403:

{ "error": "Forbidden: local network access only" }

Because this runs before authentication, a caller from outside your network never even reaches the key check. The 100.64.0.0/10 range is what lets a VPN peer connect — see VPN access.

Authentication

Every endpoint except GET /health requires the sk_local_ bearer key. The comparison is constant-time, so it doesn’t leak information through timing. A missing, malformed, or duplicated Authorization header is treated as unauthorized (401) — never a server error.

The key is generated on the device and stored in Android Keystore-backed secure storage. It never leaves the phone unless you choose to share it (for example, by scanning the pairing QR onto a client). You can regenerate it at any time; see Authentication.

Request limits

Two independent guards keep the gateway from being overwhelmed, and both are fixed in this version:

  • Send path — 60 SMS segments per minute, plus a minimum of 1 second between sends. Over the window returns 429 with Retry-After and retryAfterSeconds; a single message that could never fit the per-minute ceiling returns 400 message_too_long.
  • Per-caller burst — roughly 10 requests per second (with a small burst allowance) on every endpoint, including /health. Over that returns 429.

Request bodies are capped at 16 KB; anything larger is rejected with 413 before it’s buffered, so an oversized or open-ended request can’t exhaust memory.

Transport

On the local network the API is served over plain HTTP, not HTTPS. Hiras is honest about this: treat the API key like any LAN secret. Inbound webhooks are the exception — they’re signed with an HMAC and the key is never transmitted, so the send-capable key is never exposed over the wire even when Hiras is calling out to your backend.

Your data stays on the device

  • Send history is stored in on-device SQLite, capped at the newest 1,000 messages, and is never uploaded. You can clear it at any time; uninstalling removes it.
  • Hiras collects no analytics, no telemetry, and no crash reports. There is no cloud service behind it to send anything to.

Next