Skip to content

CDN Allowlist

The CDN allowlist (introduced in v0.15.0) tells Picora’s Markdown rewriter which http(s):// image URLs to leave alone — typically because they’re already hosted on a Picora-controlled CDN.

Without an allowlist entry, Picora can’t tell https://media.picora.me/abc.jpg from https://example.com/random.jpg — both look like third-party HTTP URLs and would be left untouched (Picora deliberately doesn’t re-host third-party images for legal reasons).

The allowlist is admin-managed because it affects all users globally.

What gets allowlisted

The allowlist primarily covers:

  • Picora’s primary CDN domains: media.picora.me, media.picora.cn
  • Picora’s video / audio domains: video.picora.me, video.picora.cn
  • Picora’s MCP domains: mcp.picora.me, mcp.picora.cn
  • Future Picora CDN regions: e.g., media-us.picora.me, media-asia.picora.me
  • Legacy domains: when migrating from old domains to new
  • User-confirmed third-party CDNs that store Picora-served images: rare; only with admin review

Default seed entries

When a fresh database is initialized, the allowlist starts with three system-managed entries:

PatternMatch typeNote
media.picora.meexactDefault Picora CF media domain
.picora.mesuffixAll *.picora.me subdomains
.picora.cnsuffixAll *.picora.cn subdomains

These cannot be deleted through the UI (the delete button is disabled with a tooltip). To remove a seed entry, you must directly modify the sys_cdn_whitelist table — but doing so risks breaking Markdown rewriting for all users; don’t.

Match types

Two match types are supported. Glob / regex are deliberately not supported (ReDoS risk).

exact

The URL’s hostname must equal the pattern, case-insensitive.

Pattern: media.picora.me
✅ media.picora.me/abc.jpg
✅ MEDIA.PICORA.ME/abc.jpg
❌ cdn.media.picora.me
❌ picora.me/abc.jpg

suffix

The URL’s hostname must end with the pattern. The pattern conventionally starts with a dot (e.g., .picora.me) to mean “any subdomain of picora.me”.

Pattern: .picora.me
✅ media.picora.me
✅ video.picora.me
✅ asia.media.picora.me
❌ picora.me (← exact match without subdomain not included; add a separate exact entry if needed)
❌ notpicora.me

Suffix matches are O(1) per entry (string comparison); the runtime cost of having 100+ entries is negligible.

Adding an entry

You’ll need an admin account (is_admin = 1 in auth_users).

  1. Open Settings → Admin → CDN Allowlist (admin-only nav item)
  2. Click Add entry
  3. Fill in:
    • Pattern: hostname or .suffix.example.com form
    • Match type: exact or suffix
    • Note: optional but strongly recommended — explain why this domain is trusted
  4. Click Save

The entry takes effect within 60 seconds globally (the API server holds the allowlist in memory and refreshes via SWR every 60s).

Editing / removing an entry

  • User-added entries (created by an admin, not seed): can be soft-deleted via Revoke. Soft-delete sets is_active = 0; the row stays in the table for audit purposes but no longer matches URLs.
  • Seed entries: cannot be removed through the UI. The delete button is disabled.

There’s no “edit” — to change a pattern, soft-delete the old entry and create a new one. This preserves audit trail.

Validation rules

The pattern field rejects:

  • URLs (e.g., https://example.com) — must be hostname only
  • Paths (e.g., example.com/foo) — pattern can’t include path
  • Special chars (*, ?, [, ], etc.) except dot
  • Leading dot for exact mode (only allowed for suffix)

Invalid patterns return 422 DOC_INVALID_PATTERN.

Use cases

Onboarding a new Picora CDN region

When Picora launches a new edge region, e.g., media-eu.picora.me:

  1. The new region’s hostname is already covered by the seed .picora.me suffix entry — no action needed
  2. If you launch a region under a different TLD (rare), add a new entry then

Recovering from a botched rewrite

If a user uploaded a Markdown document with images already hosted at a domain that was not allowlisted at the time, Picora may have re-uploaded those images. To prevent recurrence:

  1. Identify the domain (check the document’s failures field in upload response logs)
  2. Add it to the allowlist (with a note explaining the source)
  3. Future uploads from that user will skip the rewrite

Cache behavior

The allowlist is cached in two places:

  • API server in-memory: refreshed every 60 seconds (SWR)
  • ICache key cdn_whitelist_version: incremented on every change; servers detect the bump and refresh immediately

Worst case: a 60-second delay between admin action and global propagation. This is a deliberate trade-off — synchronous global invalidation would require a coordination layer that adds complexity and latency to every request.

Observability

Each allowlist operation emits structured logs:

EventLevelFields
cdn_whitelist.startup_loadedinfoentries_count, load_duration_ms
cdn_whitelist.updateinfoadmin_id, action (add/remove), pattern
cdn_whitelist.matchdebug (sampled 1%)url, matched_pattern
cdn_whitelist.refresh_failedwarn (P2)error

See Observability for log routing and alerting.

Common issues

“My new entry isn’t taking effect” — wait 60 seconds for SWR refresh. If still not, check the API server logs for cdn_whitelist.refresh_failed.

“User reports duplicate images uploaded after Markdown rewrite” — the user’s source domain isn’t allowlisted. Add it.

“422 DOC_INVALID_PATTERN when adding entry” — pattern contains special chars or includes https:// / path. Strip down to bare hostname.

“Can’t delete seed entries” — by design. Modify directly in the database only as a last resort and follow change management procedures.