AP-104: Ephemeral moderation, 24h auto-deletion, and noindex#155
Conversation
- Route ephemeral publishes to scanner_id ephemeral_tier with Llama Guard 3, script-present advisory warnings, and async Cloudflare URL Scanner verdicts - Apply artifact Platform Lockdown plus denylist when URL Scanner is malicious - Cap ephemeral upload TTL and set publish expiry from workspace auto_deletion_days - Mint content tokens with noindex and emit X-Robots-Tag plus HTML meta on responses - Export EPHEMERAL_AUTO_DELETION_DAYS from packages/config (24h)
AP-104 Ephemeral-tier moderation, short Auto Deletion, and noindex
Parent: AP-99. Blocked by AP-100 (tier state). Builds on the existing Safety Scanner seam — no new scanner machinery. OutcomeEphemeral content gets the strictest treatment: shortest Auto Deletion, Context docs
Likely files / packages
In scope
Out of scope
Acceptance criteria
Required checks
Security / operational invariants
Dependencies / blockersBlocked by AP-100. (Was needs-info — both resolved: ephemeral Auto Deletion = 24h ( Predicted file footprint / overlap
|
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
agent-paste PR preview is ready. API: https://agent-paste-api-pr-155.isaac-a46.workers.dev |
|
agent-paste PR preview is ready. API: https://agent-paste-api-pr-155.isaac-a46.workers.dev |
|
agent-paste PR preview is ready. API: https://agent-paste-api-pr-155.isaac-a46.workers.dev |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
apps/jobs/src/safety/ephemeral-scanner.ts (1)
10-10: ⚡ Quick winUpdate Llama Guard parsing: Workers AI returns
{ response: string }only;safe===falseis unsupported
@cf/meta/llama-guard-3-8bis a valid Workers AI model id, andenv.AI.run(...)returns generated text in{ response: string }. The docs describe Llama Guard as emitting plain text indicating safe vs unsafe (and category labels likeS1,S2, … when unsafe), with no documented structured boolean field (soresponse.safe === falseis likely dead unless you enable structured/JSON output). The current stringincludes("unsafe")path should be the one to rely on (or parse the category labels from the returned text).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/jobs/src/safety/ephemeral-scanner.ts` at line 10, The code currently assumes the Llama Guard output has a structured boolean (e.g., checking response.safe), but Workers AI returns plain text in { response: string } for model LLAMA_GUARD_MODEL; update the logic that calls env.AI.run(...) (the LLAMA_GUARD_MODEL call) to read the returned object's response string (e.g., result.response) and determine safety by checking the text (use response.includes("unsafe") or parse category tokens like "S1","S2", etc.) instead of relying on a response.safe boolean; remove or guard any branches that expect response.safe === false and ensure downstream variables/flags are set based on the string check.apps/jobs/src/handlers/safety-scan.ts (1)
98-105: ⚡ Quick winEphemeral enforcement runs synchronously in the consumer and re-runs on any non-quiet failure.
scanPublishedUrlMaliciousswallows its own errors, butmintAgentViewUrl,verifyAgentViewToken, andapplyMaliciousUrlLockdownare not wrapped. If any throws, it bubbles to the batchcatchand triggersmessage.retry(), which re-executesscanner.scan(the paid Llama Guard text classification) and the full URL-scan/poll on the next delivery even though warnings were already written via REPLACE-on-scan. Combined with the synchronous poll budget, each ephemeral message can block the consumer for several seconds.Consider isolating the URL-scan/lockdown step in its own try/catch (logging on failure) so enforcement failures are decoupled from the warning-write path, and decide explicitly whether a lockdown failure should retry the whole message or fail-quiet per ADR 0051.
Also applies to: 329-343
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/jobs/src/handlers/safety-scan.ts` around lines 98 - 105, The ephemeral enforcement path currently allows exceptions from mintAgentViewUrl, verifyAgentViewToken, applyMaliciousUrlLockdown (called inside runEphemeralUrlScanner / scanPublishedUrlMalicious) to bubble up and trigger message.retry() causing unnecessary re-runs of scanner.scan; wrap the entire URL-scan/lockdown sequence — specifically the calls to scanPublishedUrlMalicious, mintAgentViewUrl, verifyAgentViewToken, and applyMaliciousUrlLockdown invoked by runEphemeralUrlScanner — in its own try/catch so failures are logged (with context) and do not abort the main warning/write path; decide and implement the retry policy per ADR 0051 (either fail-quiet or requeue) inside that catch and ensure any non-retryable failures do not rethrow to the batch consumer.apps/api/src/routes/revisions.ts (1)
95-113: 💤 Low valueConsider extracting the ephemeral_tier check to avoid duplication.
The same ephemeral tier extraction logic appears twice (lines 95-99 and 109-113). While the defensive type guards are appropriate, extracting this to a local variable would improve maintainability.
♻️ Suggested refactor
const bundleStatus = bundleStatusFromPublishResult(result); + const ephemeralTier = + result !== null && + typeof result === "object" && + "ephemeral_tier" in result && + result.ephemeral_tier === true; try { await enqueuePostPublishJobs(context.env, { workspaceId: actor.workspace_id, artifactId: params.artifactId ?? "", revisionId: params.revisionId ?? "", bundleStatus: bundleStatus === "pending" ? "pending" : "disabled", requestedAt: now, - ephemeralTier: - result !== null && - typeof result === "object" && - "ephemeral_tier" in result && - result.ephemeral_tier === true, + ephemeralTier, }); } catch (error) { console.warn("Post-publish job enqueue failed after publish; revision remains published.", { artifactId: params.artifactId ?? "", revisionId: params.revisionId ?? "", bundleStatus, error: error instanceof Error ? error.message : String(error), }); } const signed = await signPublishResult(result, context.env, { workspaceId: actor.workspace_id, - ephemeralTier: - result !== null && typeof result === "object" && "ephemeral_tier" in result && result.ephemeral_tier === true, + ephemeralTier, });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api/src/routes/revisions.ts` around lines 95 - 113, The duplicated defensive check for result.ephemeral_tier should be extracted to a single local boolean and reused; create a const (e.g., ephemeralTier) computed once with the existing guards (result !== null && typeof result === "object" && "ephemeral_tier" in result && result.ephemeral_tier === true) and replace both inline checks (the enqueue warning block and the signPublishResult call) with that variable so the logic is defined in one place and easier to maintain.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/jobs/src/safety/url-scanner.ts`:
- Around line 16-17: The polling window for Cloudflare URL scans is too short
causing scanPublishedUrlMalicious to return "unknown" before a finish and
preventing runEphemeralUrlScanner from applying lockdown; increase
SCAN_POLL_ATTEMPTS and/or SCAN_POLL_DELAY_MS (e.g., poll for a longer total
duration like 10–30s per Cloudflare guidance) and ensure the loop in
scanPublishedUrlMalicious retries until task.status === "Finished" or timeout,
leaving verdict logic in runEphemeralUrlScanner unchanged so it receives a
definitive "malicious" when available; update the constants SCAN_POLL_ATTEMPTS
and SCAN_POLL_DELAY_MS (and any related loop/timeout logic) to extend the
polling window.
---
Nitpick comments:
In `@apps/api/src/routes/revisions.ts`:
- Around line 95-113: The duplicated defensive check for result.ephemeral_tier
should be extracted to a single local boolean and reused; create a const (e.g.,
ephemeralTier) computed once with the existing guards (result !== null && typeof
result === "object" && "ephemeral_tier" in result && result.ephemeral_tier ===
true) and replace both inline checks (the enqueue warning block and the
signPublishResult call) with that variable so the logic is defined in one place
and easier to maintain.
In `@apps/jobs/src/handlers/safety-scan.ts`:
- Around line 98-105: The ephemeral enforcement path currently allows exceptions
from mintAgentViewUrl, verifyAgentViewToken, applyMaliciousUrlLockdown (called
inside runEphemeralUrlScanner / scanPublishedUrlMalicious) to bubble up and
trigger message.retry() causing unnecessary re-runs of scanner.scan; wrap the
entire URL-scan/lockdown sequence — specifically the calls to
scanPublishedUrlMalicious, mintAgentViewUrl, verifyAgentViewToken, and
applyMaliciousUrlLockdown invoked by runEphemeralUrlScanner — in its own
try/catch so failures are logged (with context) and do not abort the main
warning/write path; decide and implement the retry policy per ADR 0051 (either
fail-quiet or requeue) inside that catch and ensure any non-retryable failures
do not rethrow to the batch consumer.
In `@apps/jobs/src/safety/ephemeral-scanner.ts`:
- Line 10: The code currently assumes the Llama Guard output has a structured
boolean (e.g., checking response.safe), but Workers AI returns plain text in {
response: string } for model LLAMA_GUARD_MODEL; update the logic that calls
env.AI.run(...) (the LLAMA_GUARD_MODEL call) to read the returned object's
response string (e.g., result.response) and determine safety by checking the
text (use response.includes("unsafe") or parse category tokens like "S1","S2",
etc.) instead of relying on a response.safe boolean; remove or guard any
branches that expect response.safe === false and ensure downstream
variables/flags are set based on the string check.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 2b80a0d4-5db3-4cef-a801-68dedd9c2fd9
📒 Files selected for processing (37)
apps/api/src/agent-view-html.tsapps/api/src/agent-view.tsapps/api/src/index.test.tsapps/api/src/post-publish.test.tsapps/api/src/post-publish.tsapps/api/src/routes/revisions.tsapps/api/test/route-core.test.tsapps/content/src/index.test.tsapps/content/src/index.tsapps/jobs/README.mdapps/jobs/src/env.tsapps/jobs/src/handlers/safety-scan.integration.test.tsapps/jobs/src/handlers/safety-scan.tsapps/jobs/src/safety/ephemeral-scanner.test.tsapps/jobs/src/safety/ephemeral-scanner.tsapps/jobs/src/safety/platform-lockdown.test.tsapps/jobs/src/safety/platform-lockdown.tsapps/jobs/src/safety/resolve-scanner.test.tsapps/jobs/src/safety/resolve-scanner.tsapps/jobs/src/safety/url-scanner.test.tsapps/jobs/src/safety/url-scanner.tsapps/jobs/wrangler.jsoncpackages/config/src/index.test.tspackages/config/src/index.tspackages/contracts/src/jobs.tspackages/db/src/agent-view.tspackages/db/src/artifact-invalidation.tspackages/db/src/index.tspackages/db/src/policy-ephemeral.test.tspackages/db/src/policy.tspackages/db/src/repository/upload-session-lifecycle.tspackages/db/src/repository/workflows/ephemeral-workflow.test.tspackages/db/src/repository/workflows/ephemeral-workflow.tspackages/db/src/repository/workflows/upload-publish-workflow.tspackages/db/src/resolve-access-link.tspackages/tokens/src/content.test.tspackages/tokens/src/content.ts
|
agent-paste PR preview is ready. API: https://agent-paste-api-pr-155.isaac-a46.workers.dev |
|
agent-paste PR preview resources were cleaned up. The shared Preview GitHub Environment is retained for future preview deploys. |
Summary
Implements the ephemeral-tier anti-abuse slice: shortest auto-deletion, stronger async moderation, and crawler blocking for unclaimed workspaces.
Changes
EPHEMERAL_AUTO_DELETION_DAYSfrompackages/config; cap ephemeral upload TTL to one day; on publish, setartifacts.expires_atfromworkspace.auto_deletion_daysso the existing hourly sweep expires ephemeral artifacts on schedule.ephemeral_tier): Ephemeral publishes enqueuescanner_id=ephemeral_tier(claimed tiers keepbuiltin_content). Jobs runs Llama Guard 3 on text, keeps built-in heuristics, and adds an advisoryscript_present_unclaimedwarning. Async Cloudflare URL Scanner checks the public agent-view URL; a malicious verdict creates an artifact-scoped Platform Lockdown and denylist entry.noindex: true; the content worker setsX-Robots-Tag: noindex, nofollowand injects a robots meta tag into served HTML.Ops / secrets
Configure on
jobs(documented inapps/jobs/README.md):AI(wrangler)URL_SCANNER_API_TOKENandCLOUDFLARE_ACCOUNT_IDviawrangler secret putAPI_BASE_URLfor URL Scanner target URLsVerification
pnpm verify— passpnpm test:coverage— pass (branch coverage ≥ 80%)pnpm smoke:local— fails in this environment withdatabase_unavailableduring CLI publish (harness provision succeeds but publish cannot reach DB; likely environment/harness, not introduced by this diff)Linear Issue: AP-104
Summary by CodeRabbit
Release Notes