Licensing Operations

This runbook covers day-to-day operations for acetab Pro access while Paddle is being finalized and after paid checkout goes live.

Architecture Summary

  • License verification remains server-side at /api/license/validate.
  • Promo issuance/redemption uses D1-backed APIs under /api/admin/promo/* and /api/promo/redeem.
  • Admin access is expected to be protected by Cloudflare Access policy for /api/admin/* and /admin/*.
  • The extension syncs entitlement via extension/src/services/licenseEntitlement.ts (new tab, Options, and background). Turning on PRO_FREE_MODE_* in Cloudflare alone is not enough for users who never open Settings: the client must call /api/license/validate so local isPremium updates.

Environment Variables

Set these in Cloudflare Pages project environment:

  • ADMIN_ALLOWED_EMAILS — comma-separated admin emails permitted for admin APIs.
  • ADMIN_ALLOW_DEVtrue only for local/testing without Access identity headers.
  • PRO_FREE_MODE_ENABLED — enables temporary global Pro access.
  • PRO_FREE_MODE_UNTIL — optional ISO timestamp cutoff for the free window. Leave empty (or omit) for “until disabled manually.”

Pre-Paddle Hybrid Operations

1) Temporary Global Free Window

  1. Set PRO_FREE_MODE_ENABLED=true in the Cloudflare Pages production environment (and deploy). Repository defaults in website/wrangler.toml are for local/preview only.
  2. Optionally set PRO_FREE_MODE_UNTIL to a fixed date/time; omit it to run until you flip the flag.
  3. Ship an extension build that includes automatic entitlement sync (extension/src/services/licenseEntitlement.ts).
  4. Monitor API logs for license.free_mode_validate events.
  5. Disable by setting PRO_FREE_MODE_ENABLED=false when Paddle goes live (or when the campaign ends).

2) Invite / Promo Codes for Early Adopters

  1. Create campaign with grant_type:
    • trial with default_duration_days
    • lifetime (stored as non-expiring premium license)
  2. Generate codes in batches from admin UI (/admin/licensing).
  3. Share plaintext codes through trusted channels only.
  4. Redeemers call /api/promo/redeem (or use Settings → License → Redeem promo code in the extension) and receive a full license key.

Abuse Response

If abuse is detected:

  1. Pause campaign (status=paused) or set disabled_at for compromised codes.
  2. Revoke affected licenses via /api/admin/licenses/revoke.
  3. Issue replacement codes in a new campaign.
  4. Review admin_audit_log and promo_redemptions for scope.

Paddle Cutover Checklist

When Paddle is ready:

  1. Keep promo schema and endpoints (for support grants and comps).
  2. Verify webhook env vars and route are production-ready.
  3. Disable global free mode:
    • PRO_FREE_MODE_ENABLED=false
  4. Keep invite campaigns active only where intended.
  5. Confirm paid transactions create or upgrade licenses:
    • Webhook upgrades existing promo/trial license by email where applicable.
  6. Update pricing/legal copy to clarify paid availability and invite policy.

Recovery Checklist

  • If license validation fails unexpectedly:
    • check D1 connectivity,
    • verify migration 0004_promo_licensing.sql applied,
    • verify env vars are present in production environment.
  • If admin endpoints return 401:
    • confirm Cloudflare Access policy and user email allowlist.