Files
PaperClipAI/doc/manual-e2e-private-evidence.md
T
DottaandPaperclip 018c4c9c9b Retain approved manual E2E evidence in private history (#18134)
## Thinking Path

> - Paperclip lets people manage AI agents and their work.
> - The manual AWS E2E workflow records browser and model evidence.
> - Public repository Actions artifacts are not a restricted evidence
store.
> - Some chat evidence needs the existing private-history store.
> - A target flag must not grant archive access or enable a public
fallback.
> - This pull request binds restricted runs to protected exact-source
approval and retains their standard evidence with trusted transfer code.
> - Operators can retain complete evidence without changing scanner
verdicts or scenario outcomes.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Manual AWS E2E evidence retention for exact reviewed chat-feature
selections.

**Subsystem affected**

The runner E2E manual workflow, standard evidence handoff and
private-history transfer.

**Current behavior**

Manual cell and merged evidence use Actions artifacts. In a public
repository, repository readers can download those files. Scheduled
private history does not authorize a manual target to use its role.

**Proposed behavior**

Classify sensitive selections in the trusted workflow. Require one
protected original-run approval. Verify existing private storage before
target setup or model secrets. Retain scanned standard originals and
final packaged results through a workflow-pinned runtime. Deny public
artifact and history fallback on failure.

**Reason and benefit**

Preserve inspectable standard evidence in restricted storage without
treating a private bucket as permission to retain credentials.

**Additional context**

Related source work is separate: #18067 changes paid runner routing,
#18100 changes scoped input delivery, and #17888 changes report catalog
parsing. This pull request does not include or revert those changes.

## What Changed

- Add a trusted manual classification and exact run, attempt, workflow,
target, selection and launcher approval contract.
- Add a protected manual private job. Prepare a second workflow-pinned
checkout and isolated publication dependencies before archive
credentials.
- Seal standard originals and final result bytes with exact inventories.
Reject credential contamination, unscanned carriers and changed files.
- Use existing immutable private-history archives and transfer receipts
for cells and merged reports. Keep the existing bounds.
- Use per-cell exact-key read sessions and a separate report writer.
Check every inline policy against the 2,048-character STS limit.
- Scan and seal fresh generated report logs and files with the actual
report-step credential roots before transfer.
- Require a positive nonprivate route for Actions uploads and public
history. Private failures have no public fallback. Preserve normal
catalog semantics for nonprivate selections with more than 32 IDs or
duplicate IDs.
- Authenticate generated-report fonts and every reserved trace-viewer
static asset against the exact trusted producer path and bytes,
including HTML and JavaScript. Keep standard cell evidence and unknown
carriers closed.
- Document the first eight supported IDs, deployment gates and the
operator trust boundary.

## Verification

- 57 new pure, actual-helper and actual-workflow-shell controls pass.
- 50 affected existing workflow, routing, target and private-history
controls pass. The final committed workflow security controls also pass.
- Targeted helper/test and actual launcher noEmit checks pass.
- `actionlint -shellcheck= -pyflakes=
.github/workflows/runner-full-stack-e2e.yml` passes.
- Parent `2f46336d7` required CI is green. The server 11/12 shard first
failed on database cleanup/test timing and passed one bounded recheck
with unchanged source. The original failure remains recorded.
- The preserved docs-only parent `d721c170f` had 59 terminal,
nonblocking checks and fresh Greptile 5/5. Three later review comments
exposed a genuine static-report asset failure and nonprivate selector
regression; their parent counterexamples remain recorded.
- The reviewed parent passes 76 focused controls across nine files. The
final dependency preflight successor passes all six affected controls,
including two new actual `prepare` subprocess controls, and targeted
helper/test noEmit. Unchanged suites were not repeated. A synthetic
Node-only Playwright HTML/blob/JUnit report and the real dashboard
renderer/brand stager pass the actual sealer, strict archive and
immutable receipt path; only AWS transport is synthetic. No browser or
product setup runs in that test.
- Default cell fonts/SVGs, unknown report files, forged paths, altered
static bytes, recomputed inventory tampering and registered credential
matches remain rejected. Eligibility and remaining-byte checks run
before body reads in both sealer and transfer validator; both
independent parent read-order failures are preserved.
- The pre-credential target comparison also binds the new
generated-report asset helper byte for byte. A genuine
tampered-dependency parent failure is preserved.
- Six additional actual sealer/validator controls expose the
standard-extension trace-owner bypass on the parent. All 34 affected
controls pass on its correction, including the real
producer/archive/receipt test; targeted noEmit passes. Ordinary dynamic
report HTML outside the reserved trace directory is unchanged.
- Final head `43683c4e0` passes both required gates, `ci / verify` and
`ci / e2e`, in normal CI run `40849916302` attempt 1. All 59 rollup
checks/statuses are terminal and nonblocking (55 success, two neutral,
two skipped).
- Fresh exact-head Greptile is 5/5; automated review is terminal and all
technical threads are resolved. Human CODEOWNER approval remains a
separate gate. Earlier source, genuine counterexamples and CI history
remain preserved.
- Full local build and live E2E were not run. This task prohibits local
SDK/native/server builds and paid or AWS operations. Normal pull request
CI must pass before merge readiness.
- No existing artifact bodies, archives, media or provider reasoning
were inspected. Synthetic owned fixtures prove the privacy boundaries.

## Risks

- Deployment still needs the existing private bucket, role and protected
environments. Both environments need the exact original-run approval
variable.
- Targets need the compatible handoff protocol and a separately reviewed
exact launcher digest. Existing target branches do not become ready
automatically.
- Complete duplicated originals and packaged evidence can exceed
existing archive limits. Such a transfer fails; files are not silently
removed.
- Required evidence omissions remain fatal to PASS. Safe failed evidence
is retained with an explicit missing list and incomplete
required-evidence verdict. Credential contamination still blocks all
original uploads. Restricted storage does not waive PNG, HTML, blob or
scanner checks.
- This is operator-approved source execution. Target processes can
inherit the job's OIDC request environment and may re-assume a role.
Inline session attenuation is not isolation from malicious approved
source.
- Operator authorization must bind the actual exact source and original
run. A proven administrator-controlled record may supply it without an
environment reviewer UI. No new approval UI or environment change is
required by this PR.
- Required repository CODEOWNER review is still pending. Commented
reviews and review requests are not approvals. This PR stays open and
unmerged.
- This PR does not deploy infrastructure, change IAM, dispatch
workflows, publish report payloads, change grades or transfer prior
scenario passes.

## Model Used

OpenAI `gpt-6.1-sol` in Codex. Used code execution, source analysis and
tool calls. The host does not expose the context window size.
Independent peer review used the same model family in a separate agent.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-29 08:51:40 -05:00

8.9 KiB

Restricted evidence for manual AWS E2E runs

The manual AWS workflow has a separate restricted evidence route. It uses the existing private-history bucket, protected environments, OIDC role and bounded archive transfer. It does not change the scheduled private-history lane.

GitHub Actions artifacts in a public repository are readable by repository readers. They are not an organization-private evidence store. The restricted route never uploads raw cell evidence or a merged report to Actions, S3 public history, or Pages. This stays true on setup failure, launcher failure and report failure. Previously staged files do not enable a public fallback.

Supported first selections

The first supported selection is an exact subset of these execution IDs:

  • chat-features.runner-opencode.local.activity.agent-feedback
  • chat-features.runner-opencode.local.activity.work-timeline
  • chat-features.runner-opencode.local.companies.import-preview
  • chat-features.runner-opencode.local.navigation-preferences.dismiss-announcement
  • chat-features.runner-opencode.local.tasks.completion-with-gate
  • chat-features.runner-opencode.local.tasks.private-task
  • chat-features.runner-opencode.local.secrets.personal-secret
  • chat-features.runner-opencode.local.agents.choose-primary

These IDs require the reviewed AWS Fleet route. Broad chat suites, mixed unsupported IDs, reruns and missing approvals fail closed. All chat-feature IDs are classified as restricted by trusted code. A target descriptor cannot opt out. Other manual selections keep the existing Actions route only after a trusted per-cell check confirms that the actual selected execution is not restricted. Selector whitespace and exclusivity follow the catalog producer.

A case that needs invitation-capability handling or other new producer support is not supported merely because restricted storage exists. It needs its own reviewed source, evidence contract and exact operation approval.

Deployment and operator gates

This source change alone does not configure a role, bucket, environment, target, or run. Before a supported run, the operator must:

  1. Deploy this workflow and its helpers together on the protected default branch.
  2. Keep the existing numeric actor allowlist and protected default-branch checks.
  3. Keep runner-e2e-private-paid and runner-e2e-private-history restricted to the default branch. Establish administrator-controlled approval for the exact source and original run. A reviewer UI may supply this control but is not mandatory. Prove the actual control and runtime contract before admission.
  4. Provide the existing private-history role ARN, AWS region and bucket variables in those environments. The existing role must support the existing bounded prefix and bucket-policy, ownership, public-access and retention reads.
  5. Provide RUNNER_E2E_MANUAL_PRIVATE_APPROVALS_JSON through administrator-controlled configuration available to both protected jobs. Approve the exact resolved workflow SHA, target SHA, original run ID, attempt 1, selected execution IDs and launcher SHA-256.
  6. Use a reviewed compatible target. Its private handoff and policy modules must match the workflow implementation byte for byte. Its launcher digest must match the protected approval. Earlier target branches do not gain this protocol automatically.

Classification happens before the protected job. It grants no archive authority. The administrator-controlled record must bind the actual original run ID before target dependency setup or archive credentials. Prove who can change that record and that each job consumes the exact approved record. An environment reviewer gate is one way to govern authorization; its UI is not required. A missing or stale row still fails closed before target dependency setup, archive credentials, model credentials or fixture secrets. There is no wildcard approval.

The variable has this closed shape (all values below are placeholders):

{
  "version": 1,
  "approvals": [{
    "workflowSha": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "targetSha": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
    "runId": "123456789",
    "attempt": "1",
    "executionIds": ["chat-features.runner-opencode.local.activity.agent-feedback"],
    "protocol": "paperclip.manual-private-evidence/v1",
    "launchSha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
  }]
}

Trust and credential lifetime

The workflow checks out its own immutable github.sha into a separate directory. It prepares pinned publication dependencies with --ignore-scripts and a separate store before archive credentials. Transfers use that checkout's absolute helper, tsx and archive-script paths. They do not load the target's node_modules or an ambient Node loader. The target executes only after the protected approval and private-storage preflight pass.

The AWS action returns session credentials as step outputs, not job-wide environment exports. Only trusted preflight and transfer steps receive those outputs. Transfers require an explicit complete session and disable EC2 metadata fallback. A failed or expired session fails retention. It never enables an Actions upload. Exact-key session policies limit the intended transfer calls. Each current cell uses its own two-object read session; the report writer uses a separate session. Every generated policy is checked against the 2,048-character STS limit before the credential action. No wildcard object access is added.

This is operator-approved target execution, not a sandbox for malicious source. The protected job has OIDC permission. Its target launcher can inherit the GitHub OIDC request environment, and the existing harness copies its process environment into server/native children. A target that can request a new OIDC session may re-assume the role. An inline session policy does not prevent that. Reviewers must approve the complete exact target, not just its evidence flag. This change does not alter IAM, OIDC trust, host roles or credential duration.

Evidence and failure rules

The launcher keeps its ordinary standard evidence in an owned temporary root. It preserves the original framework result before launcher result updates. It also seals final packaged results after cleanup and campaign finalization. The transfer validates exact file inventories and hashes before archiving. It excludes instance databases, workspaces, credential registries, auth files, raw capture directories and arbitrary files outside the standard evidence scope. Free-form launcher console capture is not an approved retained carrier.

The existing credential scanners and PASS PNG, HTML, blob and snapshot checks still apply. Original text, ZIP and media bytes receive those scans before retention. Raw and encoded registered roots and provider-shaped names are also rejected from original paths and inventories. Unknown extensions and SVG files that the ordinary packager does not inspect cannot become complete originals. Files cannot be silently dropped to meet a bound. Credential contamination, unsupported carriers or a changed inventory stop the transfer. Missing required PASS files still convert the attempt to infrastructure FAIL. The private inventory retains all safe available originals and records requiredEvidenceComplete: false and the exact missing list. Its complete field means the available file inventory is complete; it does not certify PASS evidence completeness. Failed framework attempts do not claim that PASS evidence checks ran. No file is synthesized and no secret-bearing original is uploaded just because the bucket is private.

The existing archive bounds remain unchanged: at most 256 MiB of source files, 4,097 archive entries and 264 MiB transferred archive bytes. Duplicated original and packaged files count toward those bounds. A run that exceeds a bound does not have complete retained evidence and needs an explicit disposition.

Private objects use immutable conditional puts and version receipts under runner-e2e-private/bounded-v1/campaigns/gha-RUN_ID-1/. Each cell has a hash-derived object name. The report reads only the approved current cells and verifies each archive against its retained transfer digest. Fresh merged HTML, blob, normalized output and report logs receive a final standard-file scan and sealed inventory with the actual current report-step credential roots, including its explicit AWS session. The report uses the same raw, encoded-root and provider-shape checks before transfer. It retains the scanned report in the same restricted store. A report error stays a report error; retention does not change its grade or manufacture a public page.

Authenticated retrieval uses the existing private-history operator machinery and approved bucket access. This PR does not grant a reader role or perform an upload. Actual deployment, environment approval, compatible target fingerprints, private storage access and one exact run approval remain operational gates.