# =============================================================================
# COMET — api/.htaccess
# =============================================================================
# Deliberately does NOT contain an AddHandler line.
#
# MEASURED on the target class of host: an AddHandler naming a PHP version that
# is not installed makes Apache serve raw .php SOURCE with a 200. This directory
# is where the SpacetimeDB credential path lives, so a wrong handler here is a
# credential disclosure. The PHP upgrade is a probe-then-commit action owned by
# api/handler-upgrade.php, which tests a candidate against probe.php ALONE
# before widening it, and rolls back if the marker does not return.
# =============================================================================

# mod_autoindex is ON by default on this host; without this, /api/ lists itself.
Options -Indexes

# Nothing in here is a directory the browser should land on.
DirectoryIndex disabled

<IfModule mod_headers.c>
  # These endpoints carry setup state that changes second to second (login
  # polling, publish results). A cached response would strand the wizard.
  Header always set Cache-Control "no-store, no-cache, must-revalidate, max-age=0"
  Header always set X-Content-Type-Options "nosniff"
  # The API is same-origin by construction. Nothing here is a document.
  Header always set X-Frame-Options "DENY"
</IfModule>

# The secrets store (api/data/comet-secrets.token) and the setup key
# (api/data/setup.key). 0600 on each file is the primary control — Apache serves
# static files as a different user than the FCGI PHP process, so it 403s — and
# api/data/ additionally carries a `Require all denied` .htaccess. This is the
# third, independent control, and it is the one that still holds if a future file
# lands with the wrong mode or the directory rule is lost.
#
# `key` is in this list for api/data/setup.key specifically: the install-root
# .htaccess only denies *.token, so without this line the claim file would rest
# on its file mode and one directory rule alone.
# `json` is for api/data/reset-throttle.json (the forgotten-password throttle,
# 0600, keyed by hashes): nothing under api/ is a JSON document a browser may
# fetch, so the whole suffix is denied rather than the one file.
<FilesMatch "\.(?i:token|key|bak|backup|tmp|log|sql|swp|json)$">
  <IfModule mod_authz_core.c>
    Require all denied
  </IfModule>
  <IfModule !mod_authz_core.c>
    Order deny,allow
    Deny from all
  </IfModule>
</FilesMatch>

# _bootstrap.php is a library, never an endpoint. Requesting it directly does
# nothing while PHP works — and leaks nothing while PHP is broken, which is the
# case this rule is actually for.
<FilesMatch "^_">
  <IfModule mod_authz_core.c>
    Require all denied
  </IfModule>
  <IfModule !mod_authz_core.c>
    Order deny,allow
    Deny from all
  </IfModule>
</FilesMatch>
