CompactorRedirects without the machinery

JSON Source Format

The reference adapter reads one UTF-8 JSON object with one redirects array. This complete example shows two independently addressable redirects:

{
  "redirects": [
    {
      "id": "docs-home",
      "canonical_url": "https://go.example.com/docs",
      "redirect_url": "https://docs.example.com/current/",
      "status_code": 308,
      "response_headers": {
        "Cache-Control": "public, max-age=300"
      }
    },
    {
      "id": "project-home",
      "canonical_url": "https://go.example.com/project",
      "redirect_url": "https://example.com/projects/compactor?ref=redirect",
      "status_code": 302
    }
  ]
}

Document field

FieldTypeRequiredConstraint
redirectsarrayyesContains zero or more redirect objects. Every object must validate.

Redirect fields

FieldTypeRequiredConstraint
idstringyesNon-empty after trimming. Must be unique in the document. Stored as an opaque stable identity.
canonical_urlstringyesAbsolute HTTP(S) public URL. Query and fragment are discarded for lookup. Its normalized value must be unique.
redirect_urlstringyesValid absolute destination URL. Its configured query is retained before any incoming query.
status_codeintegeryesOne of 301, 302, 303, 307, or 308.
response_headersobject of string valuesnoDefaults to {}. Names and values must be valid HTTP headers and cannot override protocol-owned headers.

Unknown fields are rejected at both the document and redirect level. Duplicate IDs, duplicate normalized canonical URLs, invalid destinations, unsupported statuses, and malformed or prohibited headers reject the entire source.

Status selection

StatusTypical contract
301Permanent redirect that may permit clients to change a later method according to HTTP semantics.
302Temporary redirect that may permit clients to change a later method.
303Direct the client to retrieve the destination with GET.
307Temporary redirect that preserves the request method.
308Permanent redirect that preserves the request method.

Compactor accepts only GET and HEAD, but status semantics still matter to caches and clients. Choose redirect policy explicitly; do not infer it from URL shape.

Header ownership

Compactor owns Location, Content-Length, Connection, Transfer-Encoding, Date, and Server; configuration cannot set them. The source is fully validated once at startup, and a failed initial load prevents the listener from accepting traffic. The parsed startup document is discarded.

Each later authoritative lookup reopens, parses, and validates the complete file before selecting one canonical URL. A malformed unrelated definition therefore fails the lookup; Compactor never exposes a valid subset of an invalid document. Successful definitions are cached by the runtime, while misses are not. Install complete replacements atomically to prevent readers from observing a partial write.

JSON is the reference adapter format, not the source architecture. Implementations of RedirectSource expose the same validated domain definition regardless of their external storage.