{
  "protocol": "incierge",
  "version": "0.2.0",
  "generated_at": "2026-08-16T00:52:38.377Z",
  "discovery": {
    "url": "https://incierge.jp/.well-known/incierge.json",
    "description": "Start here. Names every endpoint, the capabilities and their current status, and the open limitations."
  },
  "state": {
    "url": "https://incierge.jp/api/state",
    "description": "Derived current state. Never authored; regenerated from the log on every build, so log_head is what to compare between visits."
  },
  "evidence": {
    "url": "https://incierge.jp/api/evidence",
    "description": "Measurements bearing on claims.",
    "accepted": "A measurement with a direction (supports, contradicts, inconclusive) and the provenance of the run that produced it: system, trigger, commit, output hash.",
    "not_accepted": "An assertion without a measurement, or a measurement whose producer cannot be named."
  },
  "challenge": {
    "url": "https://incierge.jp/api/challenges",
    "method": "POST",
    "detail_url_template": "https://incierge.jp/api/challenges/{slug}.detail.json",
    "required": [
      "kind",
      "claim_target",
      "observation",
      "reproduction",
      "expected_failure"
    ],
    "optional": [
      "environment",
      "declared_source",
      "submitted_by"
    ],
    "description": "A submission is held as an untrusted observation. It changes nothing by itself.",
    "machine_projection": "Redaction is structural, not editorial. The intake computes a fixed-allowlist projection at write time (id, received_at, kind, claim_target, submission path, expected_failure after a secret scan, and booleans for what else was provided). The ingestion machine builds the public record from that projection alone — it is never given the submitter's prose, so no human review sits in the path.",
    "blocked_reasons": [
      "schema_failure",
      "insufficient_information",
      "unsafe_input",
      "no_experiment_possible"
    ],
    "pending_listing": "https://incierge.jp/api/challenges?pending=1"
  },
  "lifecycle": {
    "claim": [
      "proposed",
      "testing",
      "verified",
      "falsified",
      "retired"
    ],
    "experiment": [
      "registered",
      "running",
      "retained",
      "falsified",
      "inconclusive",
      "abandoned"
    ],
    "challenge": [
      "received",
      "classified",
      "experiment_created",
      "running",
      "completed",
      "published",
      "unactionable"
    ],
    "rules": [
      "a claim cannot be recorded without a falsifier",
      "verified is not terminal; a verified claim can later be falsified",
      "falsified is terminal — a narrowed successor must be a new claim carrying supersedes",
      "a challenge that cannot be turned into an experiment becomes unactionable and the reason is published as a limitation; it is never discarded"
    ]
  },
  "trust_model": {
    "external_input": "untrusted. Never executed, never published automatically, and never able to change a claim's status directly.",
    "internal_records": "produced by adapters from real producer output; prose is template-filled, never written per run.",
    "verification_levels": [
      "unverified",
      "self_verified",
      "independently_reproduced"
    ],
    "honest_ceiling": "No independent verifier exists yet, so evidence here reaches self_verified at best. /api/capability lists what rests on that.",
    "integrity": "Events are hash-chained. Any rewrite changes every subsequent hash, which a reader can recompute from /api/events alone.",
    "recompute": {
      "algorithm": "sha256",
      "encoding": "utf-8",
      "event_hash": "sha256 over the canonical serialisation of the event with its own `hash` key removed. `prev_hash` stays in.",
      "entity_content_hash": "sha256 over the canonical serialisation of the referenced record exactly as published at /api/<collection>/<slug>.json, with nothing removed.",
      "canonical_serialisation": [
        "JSON with no insignificant whitespace: no spaces after ':' or ','.",
        "Object keys sorted by code point, ascending, at every depth.",
        "Arrays keep their order.",
        "Strings escaped as JSON.stringify does: non-ASCII stays literal, not \\u-escaped.",
        "Numbers, booleans and null serialise as JSON.stringify emits them."
      ],
      "procedure": "Fetch /api/events. For each item in order: prev_hash must equal the previous item's hash (null for seq 1), and the recomputed hash must equal its own. The last item must equal log_head in /api/state.",
      "before_you_start": "Send a User-Agent header. A request carrying a language runtime's default (Python's urllib/*, for one) is refused at the edge with 403 and Cloudflare error 1010, before anything here is reached — see limitation/default-client-user-agents-are-refused. Any string works; curl, requests, and the named crawlers are unaffected."
    }
  }
}
