Skip to content

Replace Tinfoil sidecar with in-process Rust SDK#235

Merged
AnthonyRonning merged 9 commits into
masterfrom
codex-tinfoil-rust-sdk-opensecret
Jul 15, 2026
Merged

Replace Tinfoil sidecar with in-process Rust SDK#235
AnthonyRonning merged 9 commits into
masterfrom
codex-tinfoil-rust-sdk-opensecret

Conversation

@AnthonyRonning

@AnthonyRonning AnthonyRonning commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Replace the localhost Tinfoil Go proxy with the official tinfoil-rs SDK inside OpenSecret, removing one process and HTTP hop from completions and the other Tinfoil-backed hot paths.
  • Share one attested, origin-bound, connection-pooled SDK client across models health checks, chat completions, embeddings, audio, and transcription.
  • Preserve the existing OpenSecret request and response contract, including byte-safe SSE parsing, usage accounting, hop-header filtering, typed 503 behavior, and bounded connect-only recovery.
  • Keep the EIF entrypoint change deliberately minimal: preserve the existing OpenSecret launch, fixed post-start wait, VSOCK exposure, and forwarders; remove only the Tinfoil sidecar launch and wait, then pass the already-decoded TINFOIL_API_KEY directly to OpenSecret.
  • Remove the Tinfoil Go source, vendored binary, build recipes, nested flake, and rootfs derivations. The remaining Go toolchain support is only for the independent Continuum proxy.

Before and after network contract

The checked-in Go proxy was captured before removal, then the same credential, model, prompts, temperature, limits, and streaming settings were exercised through the Rust SDK. The normalized decrypted HTTP request sections have no diff.

Request Go baseline Rust SDK
GET /v1/models Empty-body SHA-256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855; HTTP 200; 14 models Same method/path/body hash, HTTP status, object, and exact 14-model set
Non-streaming completion Body SHA-256 1815c5ecd2c36834c59f7ce354737ad1b5bc2b93f176068c561f451957c6a717; chat.completion; exact parity-ok Same body bytes, status, content type, object, model, text, and numeric usage shape
Streaming completion Body SHA-256 d15a8b974409567cc3fef3d708406dedb99ee5578edeaf05ecc9dca9168120c8; SSE with terminal [DONE] Same body bytes, status/content type, ordered event schema, numeric final usage, and exactly one terminal [DONE]

Literal packets necessarily differ because the localhost hop is gone and TLS is randomized. The meaningful boundary is method/path/query, forwarded end-to-end headers, exact body bytes, status/schema/content type, and SSE framing. The SDK now talks directly over attested WebPKI plus SPKI-pinned, origin-bound TLS. A known byte-vector body may use Content-Length where the old Go path used chunked framing; that is an intentional transport improvement, not an API change. Full analysis is in docs/tinfoil-rust-sdk-parity.md.

Reliability and security

  • Tinfoil discovery and attestation initialize in a single background loop, so provider control-plane failure does not block backend startup or non-Tinfoil routes.
  • Each discovery attempt is bounded to 30 seconds with bounded backoff; Tinfoil routes return retryable HTTP 503 until ready.
  • DNS, TCP, or TLS connection failure triggers one single-flight rediscovery and one exact request rebuild. Request, response, body, and status errors are not replayed.
  • Concurrent failure-wave requests fail fast instead of queueing large bodies. Recovery runs in an owned task with no request/body capture, so caller timeout does not cancel shared recovery.
  • Production request construction has no generic platform-CA client; every secure execution remains inside the SDK origin-binding middleware and pinned transport.

Rollout dependency

Merge this PR together with OpenSecretCloud/opensecret-workspaces#8. The updated helper intentionally supports only this SDK backend. Existing feature workspaces must first stop services with the old helper so any running sidecar is terminated, then rebase both repositories before starting services again. The old helper expects recipes removed here; the new helper does not provide the sidecar required by old backend branches.

Verification

  • Strict Rust format and clippy checks passed
  • Backend tests: 231 passed, 17 intentionally ignored DB/live tests
  • Credentialed live Tinfoil contract test passed against clean code revision a4bedae9858e28d50cba76c4501e2bf058f854ee
  • The normalized before/after request manifests remain byte-identical for models and both completion modes
  • nix build .#default --no-link, nix flake check, entrypoint shell syntax, and git diff --check passed
  • Managed full-stack restart and smoke passed with the SDK-only workspace helper; extended health fetched models without a Tinfoil proxy service or port
  • Billing and paid Pro account validation passed; the built Maple desktop app showed Pro with 100,000 credits and returned exact chat responses
  • Fresh entrypoint-minimality, runtime, docs, SDK-helper lifecycle, secret-handling, and cross-PR reviewer passes found no unresolved actionable code issues
  • Measurement run built and uploaded both EIFs: https://github.com/OpenSecretCloud/opensecret/actions/runs/29443108395
  • Final ARM reproducibility run rebuilt dev and prod, matched both checked-in PCR files, and uploaded both artifacts: https://github.com/OpenSecretCloud/opensecret/actions/runs/29443737572

Required deployment handoff

The new pcrDev.json and pcrProd.json measurements are committed and reproducibly verified. The corresponding signed entries are not yet in pcrDevHistory.json or pcrProdHistory.json because the authorized signing private key is not available in this environment. An authorized maintainer must append both signed history entries and update any KMS or client trust configuration before deployment.

The released SDK currently verifies AMD SEV-SNP enclaves; TDX is not yet implemented. This matches the current Tinfoil deployment and is documented as an explicit future-provider constraint.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 21 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: aa6b323d-ccaf-4c96-94e9-46455b2a8e2f

📥 Commits

Reviewing files that changed from the base of the PR and between e9d5435 and f328395.

⛔ Files ignored due to path filters (2)
  • pcrDev.json is excluded by !pcrDev.json
  • pcrProd.json is excluded by !pcrProd.json
📒 Files selected for processing (3)
  • docs/local-macos-stack.md
  • docs/nitro-deploy.md
  • docs/tinfoil-rust-sdk-parity.md

Walkthrough

The backend replaces the separate Tinfoil Go proxy with an in-process Rust SDK client. Provider routes, health checks, startup configuration, deployment tooling, local workflows, and documentation now use TINFOIL_API_KEY.

Changes

Tinfoil SDK migration

Layer / File(s) Summary
Provider client and transport
Cargo.toml, src/provider_client.rs
Adds attested Tinfoil transport, shared request/response types, refresh coordination, retries, header filtering, and contract tests.
Application wiring and provider routes
src/main.rs, src/proxy_config.rs, src/web/health_routes.rs, src/web/openai.rs
Initializes ProviderClient, maps unavailable providers to HTTP 503, and routes model, completion, transcription, TTS, embedding, and health requests through it.
Streaming and parity validation
src/web/openai.rs, docs/tinfoil-rust-sdk-parity.md
Processes SSE data with byte buffers and documents request/response parity, readiness, retry, and live validation behavior.
Startup and build integration
entrypoint.sh, flake.nix, justfile, tinfoil-proxy/*
Passes the API key to the backend, removes separate Tinfoil proxy startup and build wiring, and retains Continuum proxy setup.
Environment and deployment documentation
.env.sample, docs/local-macos-stack.md, docs/nitro-deploy.md, .gitignore
Documents direct SDK configuration, removes local proxy instructions, updates Nitro guidance, and ignores Python cache directories.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant OpenSecret
  participant ProviderClient
  participant TinfoilSDK
  participant TinfoilService
  Client->>OpenSecret: OpenAI-compatible request
  OpenSecret->>ProviderClient: Build and send ProviderRequest
  ProviderClient->>TinfoilSDK: Discover and attest
  TinfoilSDK->>TinfoilService: Send provider request
  TinfoilService-->>TinfoilSDK: Response or SSE stream
  TinfoilSDK-->>ProviderClient: ProviderResponse
  ProviderClient-->>OpenSecret: Response or stream
  OpenSecret-->>Client: JSON or SSE response
Loading

Possibly related PRs

Poem

A rabbit watched the proxy hop away,
While Rusty clients began their day.
Keys tucked safe, streams flowed bright,
Attested routes took flight.
No Go proxy nest remains—
SDK hops through encrypted lanes.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.42% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: replacing the Tinfoil sidecar with an in-process Rust SDK.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex-tinfoil-rust-sdk-opensecret

Comment @coderabbitai help to get the list of available commands.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

coderabbitai[bot]

This comment was marked as resolved.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 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 `@docs/nitro-deploy.md`:
- Line 1159: Update the fenced systemd example in the documentation to include
an appropriate language tag, such as ini or systemd, immediately after the
opening fence so it satisfies markdownlint MD040.

In `@entrypoint.sh`:
- Around line 876-879: The failure handling around
tinfoil_proxy_api_key_encrypted extraction must not log secret-bearing buffers.
Remove the log statements that include tinfoil_proxy_api_key_response and the
corresponding decryption output near the related failure block, while retaining
only a non-sensitive error message.
- Around line 901-907: Update the Tinfoil Proxy API key decoding flow to capture
the exit status of base64 -d before accepting tinfoil_proxy_api_key. Reject the
key when decoding fails or produces empty output, preserving the existing error
log and exit behavior.
- Around line 917-923: Update the `/app/opensecret` startup flow to gate
readiness on an actual health/readiness check instead of the fixed `sleep 5`,
and supervise the background process so the entrypoint exits if it terminates
before becoming or while remaining ready. Preserve the existing environment
variables and startup logging while ensuring `socat` starts only after readiness
succeeds.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 22b57b6a-0425-4bac-afc1-08b0290c5c77

📥 Commits

Reviewing files that changed from the base of the PR and between c9c5ccd and e9d5435.

⛔ Files ignored due to path filters (2)
  • pcrDev.json is excluded by !pcrDev.json
  • pcrProd.json is excluded by !pcrProd.json
📒 Files selected for processing (5)
  • docs/nitro-deploy.md
  • docs/tinfoil-rust-sdk-parity.md
  • entrypoint.sh
  • src/main.rs
  • src/provider_client.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/tinfoil-rust-sdk-parity.md
  • src/provider_client.rs
  • src/main.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 4

🤖 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 `@docs/nitro-deploy.md`:
- Line 1159: Update the fenced systemd example in the documentation to include
an appropriate language tag, such as ini or systemd, immediately after the
opening fence so it satisfies markdownlint MD040.

In `@entrypoint.sh`:
- Around line 876-879: The failure handling around
tinfoil_proxy_api_key_encrypted extraction must not log secret-bearing buffers.
Remove the log statements that include tinfoil_proxy_api_key_response and the
corresponding decryption output near the related failure block, while retaining
only a non-sensitive error message.
- Around line 901-907: Update the Tinfoil Proxy API key decoding flow to capture
the exit status of base64 -d before accepting tinfoil_proxy_api_key. Reject the
key when decoding fails or produces empty output, preserving the existing error
log and exit behavior.
- Around line 917-923: Update the `/app/opensecret` startup flow to gate
readiness on an actual health/readiness check instead of the fixed `sleep 5`,
and supervise the background process so the entrypoint exits if it terminates
before becoming or while remaining ready. Preserve the existing environment
variables and startup logging while ensuring `socat` starts only after readiness
succeeds.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 22b57b6a-0425-4bac-afc1-08b0290c5c77

📥 Commits

Reviewing files that changed from the base of the PR and between c9c5ccd and e9d5435.

⛔ Files ignored due to path filters (2)
  • pcrDev.json is excluded by !pcrDev.json
  • pcrProd.json is excluded by !pcrProd.json
📒 Files selected for processing (5)
  • docs/nitro-deploy.md
  • docs/tinfoil-rust-sdk-parity.md
  • entrypoint.sh
  • src/main.rs
  • src/provider_client.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/tinfoil-rust-sdk-parity.md
  • src/provider_client.rs
  • src/main.rs
🛑 Comments failed to post (4)
docs/nitro-deploy.md (1)

1159-1159: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language tag to the fenced systemd example.

markdownlint reports MD040 for this fence. Use an appropriate tag such as ini or systemd.

🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 1159-1159: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 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 `@docs/nitro-deploy.md` at line 1159, Update the fenced systemd example in the
documentation to include an appropriate language tag, such as ini or systemd,
immediately after the opening fence so it satisfies markdownlint MD040.

Source: Linters/SAST tools

entrypoint.sh (3)

876-879: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not log secret-bearing buffers on failure.

$tinfoil_proxy_api_key_response contains encrypted secret material, while $decryption_output can contain the plaintext key. Logging either buffer exposes credentials to retained logs. This is the same issue raised previously, but both logs remain in the current code.

Also applies to: 893-897

🤖 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 `@entrypoint.sh` around lines 876 - 879, The failure handling around
tinfoil_proxy_api_key_encrypted extraction must not log secret-bearing buffers.
Remove the log statements that include tinfoil_proxy_api_key_response and the
corresponding decryption output near the related failure block, while retaining
only a non-sensitive error message.

901-907: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '\n== entrypoint slice ==\n'
file='entrypoint.sh'
sed -n '890,915p' "$file"

printf '\n== locate decoder usage ==\n'
rg -n 'base64 -d|tinfoil_proxy_api_key|decrypted_api_key' "$file"

printf '\n== base64 malformed-input probe ==\n'
python3 - <<'PY'
import subprocess, textwrap, shlex

cases = [
    ("valid", "YWJj"),
    ("invalid_tail", "YWJj$"),
    ("invalid_mid", "YW$Jj"),
    ("invalid_padding", "YQ==="),
    ("empty", ""),
]
for name, s in cases:
    p = subprocess.run(
        ["bash", "-lc", f"printf %s {shlex.quote(s)} | base64 -d 2>/dev/null"],
        capture_output=True, text=True
    )
    print(f"{name}: status={p.returncode} decoded={p.stdout!r}")
PY

Repository: OpenSecretCloud/opensecret

Length of output: 2826


Check the base64 decoder’s exit status before using the API key. base64 -d can emit partial output on malformed input, so the current emptiness-only guard can let a bad key through. Require a zero exit status and non-empty output. entrypoint.sh:901-907

🤖 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 `@entrypoint.sh` around lines 901 - 907, Update the Tinfoil Proxy API key
decoding flow to capture the exit status of base64 -d before accepting
tinfoil_proxy_api_key. Reject the key when decoding fails or produces empty
output, preserving the existing error log and exit behavior.

917-923: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant section of entrypoint.sh with line numbers
sed -n '880,960p' entrypoint.sh | cat -n

# Search for supervision/readiness-related helpers and opensecret process handling
rg -n -C 2 -e '\b(run_forever|wait|kill -0|curl|health|ready|vsock)\b|/app/opensecret|opensecret' entrypoint.sh

Repository: OpenSecretCloud/opensecret

Length of output: 11951


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the full file size before reading more if needed
wc -l entrypoint.sh

# Map the script's structure to find any functions that supervise child processes
ast-grep outline entrypoint.sh --view expanded

Repository: OpenSecretCloud/opensecret

Length of output: 210


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the restart-on-exit helper and the startup patterns around opensecret and other services
sed -n '1,80p' entrypoint.sh | cat -n
printf '\n---\n'
sed -n '520,580p' entrypoint.sh | cat -n
printf '\n---\n'
sed -n '840,880p' entrypoint.sh | cat -n

Repository: OpenSecretCloud/opensecret

Length of output: 7004


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the end of entrypoint.sh for any wait/monitoring of opensecret or a final exec
tail -n 80 entrypoint.sh | cat -n

# Search for explicit waits or signal traps in the script
rg -n -C 2 -e '^\s*wait\b|trap\b|kill\b|opensecret|/app/opensecret|socat|exec ' entrypoint.sh

Repository: OpenSecretCloud/opensecret

Length of output: 6247


Add readiness gating and supervise /app/opensecret.
/app/opensecret is started in the background and only given a fixed 5s delay before socat begins. A slower startup can race the first requests, and an early exit leaves the entrypoint running without a backend.

🤖 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 `@entrypoint.sh` around lines 917 - 923, Update the `/app/opensecret` startup
flow to gate readiness on an actual health/readiness check instead of the fixed
`sleep 5`, and supervise the background process so the entrypoint exits if it
terminates before becoming or while remaining ready. Preserve the existing
environment variables and startup logging while ensuring `socat` starts only
after readiness succeeds.

@AnthonyRonning
AnthonyRonning merged commit 7313357 into master Jul 15, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant