Student benefits - deployment and updating

Deploy this directory layout (PHP 8.1+, curl, DOM, mbstring, HTTPS):
  hosting-account/
    public_html/             web document root
    server/                  PHP application code, outside document root
    scripts/                 CLI maintenance tools, outside document root
    private/student-benefits/
      catalog.json           curated offers plus private verification evidence
      sources.json           source URLs and per-source redirect host allowlists
      monitor-state.json     generated last-check/hash/error/review flags
      review-queue.json      generated unverified discovery links

Do NOT move private or scripts under public_html. Give PHP read access to the
private JSON and the scheduled CLI process write access to that directory.
HAPITARON_BENEFITS_DIR optionally overrides the private data path. The application
code is AGPL; private operational configuration and content data are not shipped
in the public source archive. The public API deliberately selects only visitor
fields; discovery-source names, URLs, hashes and raw notes are never serialized.
Public descriptions and official redemption links necessarily remain visible.
The source archive contains the server code, not the live private JSON files.

The category opens at /student-benefits (Apache rewrite) or
/student-benefits.php. It is linked from the homepage and the Students group.

Initial catalog:
28 reviewed offers/programs relevant to Israel, checked on 2026-10-11.
This is a starter catalog, not every offer from every directory. Public records
must have region IL, _internal.israelEligible=true and israelEvidenceUrl.
Foreign-only or unreviewed country eligibility is excluded by the public API
and rejected by the publishing command. Global programs are listed only when
their eligibility also covers Israeli institutions; individual verification
by the provider is still required. GitHub Copilot is part of the GitHub pack,
with a separate card explaining activation, not an extra cumulative benefit.
Partner cards belonging to the same bundle may share a redemption URL when
_internal.redemptionGroup matches. They do not represent cumulative entitlements.
Offers older than 30 days become "needs review". Expiration dates are enforced
at request time, even when the scheduler stops. Exact prices are not invented.

Checking for updates:
  php /absolute/project/path/scripts/update-benefits.php

Configure a DAILY cron job in the hosting control panel to run that command.
This repository does not itself install or activate a hosting cron job.
Use the host's PHP CLI path, redirect output to a private log outside the web
root, and configure the host to notify the owner on nonzero exit codes.
Exit 0: all sources checked. Exit 2: one or more sources could not be checked.
Exit 1: setup, concurrent run, or storage failure.

The collector checks robots.txt, uses bounded HTTPS requests and approved
redirect hosts, fingerprints the main readable text, and extracts candidate
HTTPS links from discovery pages. JavaScript-only/login-only/blocked pages are
flagged for manual review; there is no bypass. It does not magically interpret
every website or automatically translate/publish unverified offer conditions.
First successful fetch establishes a fingerprint. A hash change or fetch error
marks the associated offer for review. Unchanged checks do NOT advance the human
verification date. Discovery links are canonicalized to remove tracking params
and deduplicated in the private queue. Existing records survive failures.

Review and publish without editing website JS/HTML:
1. Read private/student-benefits/review-queue.json and monitor-state.json.
2. Verify the offer on its official site: eligible countries and students,
   cost, duration, enrollment deadline, renewal, and redemption steps.
3. Prepare a JSON record with fields matching a catalog item. Include
   _internal.officialEvidenceUrl, _internal.monitorId, _internal.israelEligible
   and _internal.israelEvidenceUrl. Verify Israel eligibility explicitly;
   source discovery alone never establishes it. Keep publication drafts
   private; use Hebrew summaries written for this site, not copied articles.
4. Run:
   php /absolute/project/path/scripts/publish-benefit.php /private/reviewed-offer.json
   This validates the record, stamps the human verification date, updates the
   catalog by stable ID, and acknowledges the associated monitor alert.
5. For a new provider, add its official page as a monitor in sources.json.
   Only explicit HTTPS hosts are followed; add known redirect hosts if needed.

No paid API, database, or third-party browser call is required. Collection runs
on YOUR hosting server; unlike PDF processing it cannot be browser-only if the
discovery-source configuration must be hidden from visitors.

Checks:
  php tests/benefits.php
  node tests/benefits-browser.cjs
Browser checks require Playwright, Chrome, and PHP_BINARY pointing to php.exe
on Windows or the PHP CLI on another host. Live sources are not needed by tests.

Before deployment: regenerate the public source bundle with
  powershell -File scripts/package-source.ps1
The script includes server application source but never private data or logs.
This must be regenerated after future code changes, not after catalog updates.
