GunSpec
Security

Security

How access to the API is decided, how a credential can fail and what that tells you, what we do when something breaks, and how to report a weakness. The mechanics behind each request live on How we protect your data.

  • 118/135operations need a credential
  • 2ways to send a key
  • 39reasons a request can be refused
  • TLSHSTS with preload

Four rules, each with the code that enforces it.

Least privilege

A key sees what its plan allows and nothing more. Fields are shaped per plan after every handler; endpoints are gated by tier in one middleware.

Field shaping

Defence in depth

TLS, hardening headers, an origin allow-list, body limits, per-minute and daily limiters and pagination caps stack; a request that gets past one still meets the next.

The request pipeline

Data minimisation

An account is an email and a plan. Card details stay with Stripe, error logs hold a code and a request id, staff audit rows never store a body.

Privacy and compliance

Secure by default

There is one configuration and it is the safe one: nothing for you to switch on, and the only opt-in on our side is analytics behind a consent banner.

Headers on every response

Four kinds of credential reach the API. Each is verified differently and fails with its own reasons, so the error tells you which one is at fault.

CredentialVerified byFails as
API keyLooked up in the key store on every request, with its plan and owner. Disabled and expired keys are refused before any handler runs. Sent as X-API-Key or Authorization.
Website sessionAn HttpOnly, Secure cookie on our domain, backed by a database row and cached; a cache miss rebuilds it from the row rather than signing you out. Separate from API keys and from the staff console's own session.
The website itselfA server-side proxy token the browser never sees. Grants no plan and no key: the site renders with the full field policy but cannot be borrowed to call the API.
Marketplace proxyA proxy secret plus the subscription header the marketplace forwards. The plan name maps to a tier; the marketplace meters the quota.

A 401 is a credential problem: missing, malformed, disabled or expired, and presenting a different one can work. A 403 means the credential is valid and the caller is not permitted; rotating a key changes nothing. Every response names the exact reason.

Every reason, with what to do

What keeps a key a secret.

Do

  • Send a key only over TLS, in the X-API-Key header or as a Bearer token, from server-side code.
  • Use one key per application or environment, so revoking one does not take down the others.
  • Rotate keys on a schedule and revoke immediately when someone leaves; revocation is instant.
  • Read error.reason before rotating: a 403 is never fixed by a new key.
  • Keep the key out of the URL: query strings land in logs and referrer headers.

Do not

  • Ship a key in browser or mobile code, a public repository, or a shared screenshot.
  • Reuse one key across several products, or share it between people.
  • Send the same key from many unrelated clients: when 3 or more keys share one client fingerprint the account is flagged, and one key spread across many looks the same.
  • Rely on the playground's stored key for production: it lives in that browser only and is never sent to us for storage.

What is in place before an incident, and what you will see during one.

Status page

Live and historic availability at status.gunspec.io. The footer of every docs page shows the current state.

Kill switch, maintenance, read-only

Operators can pause or freeze the public API without touching their own console. You see a 503 with Retry-After and a named reason.

Error alarm

An hourly check compares 5xx rates and pages operators on a spike. A deliberate 5xx is logged like an outage, so nothing we raise on purpose is invisible to us.

Request ids

Every response carries X-Request-Id and every error echoes it. Quote it; it finds the request, its status and its error code in our logs.

Staff audit trail

Every mutating staff action is written to an append-only log with actor, target and origin. Bodies are never stored.

Sealed secrets and backups

Operator credentials are encrypted with authenticated encryption before they reach the database, so a copied backup is not a compromised account.

We want to hear about it, privately and first. The full policy is on the main site; this is the short form.

  1. 1Email security@gunspec.io with a clear description, the steps to reproduce, the affected URL or endpoint, and any proof of concept.
  2. 2Include an X-Request-Id if you have one; it takes us straight to the request.
  3. 3One issue per message where practical. We acknowledge, keep you updated, and credit you with your permission.

In scope

  • gunspec.io, api.gunspec.io and the systems we operate to deliver them.

Out of scope

  • Denial of service, volumetric or spam testing.
  • Social engineering of staff or users, and physical attacks.
  • Reports from automated tools without a demonstrated, exploitable impact.
  • Our hosting and payment providers, which run their own programmes.

Safe harbour

Good-faith research that follows the policy is authorised. We will not pursue legal action for it, and if a third party does, we will make that authorisation known. Use test accounts and test data, do not touch data that is not yours, and give us reasonable time to fix before disclosing.

Recognition

No paid bounty programme today. Valid reports are acknowledged publicly, with your permission.