# auth.md — Authentication on ASKGIB (askgib.com)

- **Title:** Authentication on ASKGIB
- **Description:** There is no authentication on askgib.com. Reads are open; writes are same-origin, rate-limited and anonymous. How an agent discovers this, step by step.
- **Canonical:** https://www.askgib.com/auth.md
- **Last updated:** 2026-10-13

There is none. askgib.com is a personal publication with no accounts, no
tokens, and no OAuth server. The discovery documents at
`/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server`
exist so that an agent can find this out programmatically; they advertise one
identity type: `anonymous`.

## Reads

Open to anyone, no header needed, cached for 60 seconds.

- `GET /api/articles` — every published article: slug, title, date, excerpt, reading time, URL.
- `GET /api/articles?slug=<slug>&lang=<code>` — one article with its Markdown body. Languages: en, fr, es, pt, it, de, ru, ja, ko, zh, ar, he, hi, tr, kab.
- The `url` in both answers is the article's page: `/articles/<slug>` in English, `/<code>/articles/<slug>` in another language (`/fr/articles/<slug>`). The same URL with `.md` is its Markdown.
- `GET /api/votes?slug=<slug>` — the caller's own votes on one article and its comments (`{ article, comments }`), matched on the same anonymous IP hash a vote is stored under. Never cached; no hash is returned.
- `GET /llms.txt` — what the site is, when to use it, and the article index.
- `GET /api/agent/identity` — this document's machine-readable summary.

## Writes

The page's own forms post to these; they accept **same-origin requests only**,
are rate-limited per IP hash, and may require a Cloudflare Turnstile token
when captcha is enabled. A reader is identified by an anonymous, salted hash
of their IP, shown as a colour-and-animal name. No raw IP is ever stored.

- `POST /api/topic-requests` — suggest an article topic (5 per day).
- `POST /api/bug-reports` — report a bug on the site (5 per day; not publicly readable).
- `POST /api/comments` — comment on an article (100 per day).
- `POST /api/votes` — vote on an article or comment.
- `POST /api/city-submissions` — add a city to the Beer Index (3 per day).

An agent acting from outside the page cannot call these directly; it should
send the user to the page (`/answers` for a topic request) or, in a WebMCP-capable
browser with the page open, use the in-page tools `request_topic`, `list_articles`,
`read_article`, `set_display_mode`, `set_language`.

## Identity types

`identity_types_supported: ["anonymous"]`. No assertions, no service auth.

## Walkthrough overview

The sections below follow the auth.md walkthrough: Discover, Pick a method,
Register, Claim, Exchange, Use credential, Errors, Revocation. Five of them do
not apply here, because no credential is ever issued; each one says so.

## Discover

Fetch `/.well-known/oauth-protected-resource` (RFC 9728) or
`/.well-known/oauth-authorization-server` (RFC 8414). Both answer 200 with
JSON: no bearer methods, no scopes, no grant types, and
`agent_auth.identity_types_supported: ["anonymous"]`. The API itself is
described at `/openapi.json`, with `security: []` on every operation.

## Pick a method

There is one method: `anonymous`. Send requests with no `Authorization`
header, no cookie and no key.

## Register

Not applicable: there are no accounts, no client registration and no dynamic
client registration endpoint. Nothing to sign up for.

## Claim

Not applicable: no identity is claimed or verified. The server derives an
anonymous display name from a salted hash of the IP address; the raw IP is
never stored.

## Exchange

Not applicable: no credentials are issued, so there is no token endpoint and
no code, assertion or refresh token to exchange.

## Use credential

Not applicable: no credentials are issued. Call the reads directly, for
example `curl -s https://www.askgib.com/api/articles`.

## Errors

No 401 or 403 is ever caused by missing credentials. Every error is JSON:
`{ "error": "<message>" }`, plus `requestId` on 5xx failures.

- `403` on a POST: the `Origin` header is missing or is not the site's own
  origin. Writes accept same-origin browser requests only; there is no
  credential that lifts this.
- `400`: invalid body, or a missing or rejected captcha token.
- `413` / `415`: body over 16 KiB, or `Content-Type` is not `application/json`.
- `429`: rate limit reached, or the IP hash is temporarily banned. The
  response carries `Retry-After` in seconds, plus the `RateLimit` headers.
- `404` on a POST: the article or comment named in the body does not exist
  (a comment or vote for a slug that is not a published article).
- `404` on an unknown `/api/...` path: JSON with `code`, `message`, `hint`,
  pointing at `/openapi.json` and `/developers`.

## Rate limit headers

Every `/api` response carries `RateLimit-Policy`, the list of quotas this API
enforces. The four write routes add `RateLimit`, `RateLimit-Limit`,
`RateLimit-Remaining` and `RateLimit-Reset` for the quota that request spent.
Reads spend no quota, so they carry the policy list alone. A `429` adds
`Retry-After`. The windows are sliding, so `RateLimit-Reset` states the window
length: the worst case before the quota is free again.

## API version

The API is stable and follows semantic versioning. Every `/api` response
carries `API-Version` (now `1.0.0`), the same value as `info.version` in
`/openapi.json`. The paths carry no version prefix: the unversioned paths are
version 1. A breaking change ships under a new path prefix; the operation it
replaces is first marked `deprecated: true` in `/openapi.json` and answers with
the `Deprecation` (RFC 9745) and `Sunset` (RFC 8594) headers for at least 90
days before it is removed.

## Revocation

Not applicable: no credentials are issued, so there is nothing to revoke and
no revocation endpoint. A temporary ban on an IP hash expires on its own.
