{
  "name": "L8 Protocol",
  "version": "0.1",
  "status": "draft",
  "description": "Trustless webhook delivery via Ed25519 challenge-response handshake. No shared secrets. Ownership proven once, trust cached locally. All deliveries carry cryptographically signed headers the receiver verifies without any network call.",
  "algorithm": "ed25519",
  "nonce_ttl_seconds": 300,

  "receiver_endpoints": [
    {
      "method": "GET",
      "path": "/.well-known/l8",
      "description": "Returns receiver identity and public key. Required to participate in L8.",
      "response": {
        "protocol_version":   "string — semver, e.g. '0.1'",
        "service_name":       "string — human-readable name",
        "public_key":         "string — base64 Ed25519 public key (32 bytes raw)",
        "challenge_endpoint": "string — path or full URL, e.g. '/l8/challenge'",
        "supported_algorithms": ["ed25519"],
        "capabilities":       ["signed_payloads"]
      }
    },
    {
      "method": "POST",
      "path": "/l8/challenge",
      "description": "Challenge-response handshake. Sender proves it owns its private key, receiver proves it owns its private key. Trust established on both sides.",
      "request": {
        "challenge_id":      "string — UUID, generated by sender",
        "nonce":             "string — UUID, single-use",
        "timestamp":         "integer — Unix seconds, must be within 300s of receiver clock",
        "sender_public_key": "string — base64 Ed25519 public key",
        "signature":         "string — base64 Ed25519 sig of '{challenge_id}:{nonce}'"
      },
      "response": {
        "challenge_id":        "string — echoed back",
        "nonce":               "string — echoed back",
        "receiver_signature":  "string — base64 Ed25519 sig of '{challenge_id}:{nonce}'",
        "receiver_public_key": "string — base64 Ed25519 public key"
      },
      "reject_if": [
        "timestamp older than 300 seconds",
        "nonce has been seen before (replay protection)"
      ]
    }
  ],

  "signed_delivery_headers": {
    "X-L8-Delivery-Id": "UUID — unique per delivery",
    "X-L8-Timestamp":   "Unix seconds as string",
    "X-L8-Key-Id":      "base64 of first 8 bytes of sender public key",
    "X-L8-Signature":   "base64 Ed25519 sig of '{delivery_id}.{timestamp}.{base64(sha256(body))}'"
  },

  "verification": {
    "message_format": "{X-L8-Delivery-Id}.{X-L8-Timestamp}.{base64(sha256(body))}",
    "algorithm": "Ed25519 verify(sender_public_key, message, X-L8-Signature)",
    "sender_public_key_source": "obtained during handshake or from sender's /.well-known/l8",
    "on_failure": "re-fetch sender's /.well-known/l8 in case of key rotation, then retry once before rejecting"
  },

  "trust_cache": {
    "description": "One file per trusted domain. Delete to force re-handshake.",
    "filename_convention": "l8-trust/{domain}.json",
    "file_format": {
      "domain":           "string — e.g. 'https://example.com'",
      "public_key":       "string — base64 Ed25519 public key",
      "validated_at":     "integer — Unix seconds",
      "protocol_version": "string",
      "capabilities":     ["signed_payloads"]
    }
  },

  "graceful_degradation": "If /.well-known/l8 returns non-200, delivery proceeds without L8 headers. Receivers that do not implement L8 are unaffected.",

  "key_management": {
    "env_var": "L8_PRIVATE_KEY — base64 raw Ed25519 private key (64 bytes). Takes priority over key file.",
    "key_file": ".l8-key — raw Ed25519 private key bytes. Auto-generated on first start if neither env var nor file exists.",
    "public_key_derivation": "Public key is always derived from the private key. Only one secret to manage."
  },

  "future_versions": {
    "0.2": "Payload encryption using receiver public key (ECIES / X25519)",
    "0.3": "Formalized key rotation and trust expiry",
    "1.0": "Stable protocol — no breaking changes"
  },

  "reference_implementations": [
    {
      "name": "Aquifer (Go)",
      "url": "https://github.com/rjpruitt16/aquifer",
      "role": "sender"
    },
    {
      "name": "L8 test receiver (Python)",
      "url": "https://github.com/rjpruitt16/aquifer/blob/main/tests/l8_receiver.py",
      "role": "receiver"
    }
  ],

  "spec_url": "https://rjpruitt16.github.io/l8-protocol/spec.json"
}
