Skip to content

chore: add self-report demo app (vanilla PHP) - #23

Merged
tomasstark merged 10 commits into
mainfrom
feat/self-report-demo-site
Jul 14, 2026
Merged

chore: add self-report demo app (vanilla PHP)#23
tomasstark merged 10 commits into
mainfrom
feat/self-report-demo-site

Conversation

@tomasstark

@tomasstark tomasstark commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

This PR adds demo/self-report/ — a minimal vanilla-PHP publisher for testing the /.well-known/supertab/status self-report endpoint end-to-end against the real sandbox API, deployed as a persistent public target on Fly.io (hostname kept internal; see the sandbox merchant-site registration). It doubles as the canonical "plain PHP" integration reference: every request flows through SupertabConnect::handleRequest(), so the status endpoint works with zero endpoint-specific code.

Context

The backend's live-health probe needs a real, registered origin to mint challenges against (aud = origin, signed by the platform JWKS). The existing demo/ CLI demo is self-contained with a mock API, so it can't exercise this. This app pins the released Packagist SDK (1.4.0-beta.9, which ships the component: {kind: "php-sdk"} identity) — validating a new release is a pin bump + fly deploy.

Key Changes

  • index.php (~100 lines, no framework): X-Forwarded-Proto scheme fix-up (TLS-terminating proxies forward plain HTTP; without it every probe's aud mismatches and gets the decoy) → /healthz short-circuit (health checks never look like traffic) → everything else through handleRequest(), emitting Allow as a demo HTML page and Block/Respond verbatim.
  • Env-var config: SUPERTAB_MERCHANT_API_KEY (required, fail-fast 500), SUPERTAB_BASE_URL (defaults sandbox), SUPERTAB_ENFORCEMENT, SUPERTAB_ANALYTICS — so the payload's enforcement/eventReporting can be flipped per test without code changes.
  • Dockerfile: multi-stage composer install from the committed lockfile; php:8.3-apache on port 8080 with FallbackResource /index.php (dot-prefixed paths route to the front controller without mod_rewrite) and a SetEnvIf restoring the Authorization header, which Apache/mod_php withholds from $_SERVER (root fix in fix: read Authorization via getallheaders() when Apache withholds it from $_SERVER #24).
  • fly.toml: one always-warm machine (auto_stop_machines = 'off', min_machines_running = 1) so backend probes never hit a cold start; HTTP health check on /healthz.
  • DEPLOY.md/README.md: Fly runbook — setup, smoke checks, sandbox registration, update cadence.

Verified end-to-end: the deployed instance passed the backend's live self-report check — challenge verified against the sandbox platform JWKS, 200 payload with component: {kind: "php-sdk", version: "v1.4.0-beta.9"}. Along the way this rig surfaced the Apache Authorization-header bug now fixed in #24.

(Deployment history: an AWS App Runner variant was the original plan; it was dropped over iam:PassRole friction — the App Runner runbook is retrievable from this branch's git history.)

tomasstark added a commit that referenced this pull request Jul 14, 2026
…from $_SERVER (#24)

This PR makes `RequestContext::fromGlobals()` fall back to
`getallheaders()` for the `Authorization` header, fixing silently-broken
verification on Apache with mod_php.

## Context

Apache/mod_php withholds the `Authorization` request header from the
CGI-style `$_SERVER` variables (a CGI-era security behavior), while
`getallheaders()` still exposes it. `fromGlobals()` read only
`$_SERVER['HTTP_AUTHORIZATION']` / `REDIRECT_HTTP_AUTHORIZATION`, so on
vanilla Apache — one of the most common PHP deployments — every bearer
looked absent. Because verification fails closed, nothing errored:
status probes got the `404 {"supertab": true}` decoy and license-token
checks treated licensed bots as unlicensed.

Found live: the self-report demo site (#23) deployed on `php:8.3-apache`
returned only the decoy to backend health probes. Empirically confirmed
in the container — `getallheaders()` carried the challenge JWT while
`$_SERVER` had nothing. The demo image works around it with `SetEnvIf`;
this PR fixes the SDK so integrators don't need to know the incantation.

## Key Changes

- New pure helper `RequestContext::resolveAuthorizationHeader(array
$server, array $requestHeaders): ?string` — `$_SERVER` still wins;
otherwise the raw request headers are searched case-insensitively for
`Authorization`. `fromGlobals()` feeds it `getallheaders()` when the
SAPI provides the function (guarded by `function_exists`; the FPM and
built-in-server SAPIs have it too, plain CLI doesn't).
- 5 unit tests covering precedence, both fallbacks, case-insensitive
matching, and the absent-everywhere null.

Verified on real Apache: ran the demo container with its `SetEnvIf`
workaround removed and this patch applied —
`$_SERVER['HTTP_AUTHORIZATION']` unset,
`fromGlobals()->authorizationHeader` correctly returns the bearer. Full
suite green (286 tests).

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@tomasstark
tomasstark requested a review from Copilot July 14, 2026 12:44
@tomasstark tomasstark changed the title Add self-report demo site (vanilla PHP publisher for status-probe testing) chore: add self-report demo app (vanilla PHP) Jul 14, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new demo/self-report/ vanilla-PHP “publisher” app intended to serve as a persistent public target for end-to-end testing of the SDK-served /.well-known/supertab/status self-report endpoint against the real sandbox API, and as a minimal integration reference.

Changes:

  • Introduces a tiny front controller (index.php) that fixes proxy scheme, short-circuits /healthz, and delegates all other traffic to SupertabConnect::handleRequest().
  • Adds container + deployment artifacts (Dockerfile, Fly.io config) and runbooks for local + hosted usage.
  • Pins the demo to the released Packagist SDK version via composer.json/composer.lock.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
demo/self-report/README.md Documents purpose, config, local run steps, and probe expectations.
demo/self-report/index.php Minimal front controller routing requests through the SDK and rendering a small “allowed” page.
demo/self-report/fly.toml Fly.io config to keep an always-warm instance for probing.
demo/self-report/Dockerfile Multi-stage build that installs Composer deps and configures Apache to route all requests to index.php.
demo/self-report/DEPLOY.md Fly.io deployment runbook and operational notes.
demo/self-report/composer.lock Locked dependency set for reproducible demo builds.
demo/self-report/composer.json Demo project definition + pinned SDK requirement.
demo/self-report/.gitignore Ignores vendor directory.
demo/self-report/.dockerignore Excludes vendor and README from Docker build context.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread demo/self-report/README.md Outdated
Comment thread demo/self-report/index.php Outdated
Comment thread demo/self-report/index.php Outdated
Comment thread demo/self-report/Dockerfile Outdated
@tomasstark
tomasstark merged commit e1977c5 into main Jul 14, 2026
9 of 10 checks passed
@tomasstark
tomasstark deleted the feat/self-report-demo-site branch July 14, 2026 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants