Skip to content

feat(sdk): add TDF decrypt convenience helpers - #3828

Merged
marythought merged 6 commits into
mainfrom
feat/dspx-3373-decrypt-helpers
Aug 4, 2026
Merged

feat(sdk): add TDF decrypt convenience helpers#3828
marythought merged 6 commits into
mainfrom
feat/dspx-3373-decrypt-helpers

Conversation

@marythought

@marythought marythought commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds DecryptBytes, DecryptTo, and DecryptFile on SDK — thin convenience wrappers over LoadTDF + Reader.WriteTo for the common decrypt-to-plaintext case.

  • All three take ctx context.Context as their first parameter, governing the KAS rewrap request (the only network call each makes). LoadTDF/Reader.WriteTo don't accept a context, so ctx is threaded in via reader.Init(ctx) right after LoadTDF succeeds — Init does the rewrap under ctx, and the subsequent WriteTo call skips its own key unwrap since the payload key is already set.
  • New sentinel errors ErrTDFNotDecryptable / ErrTDFDecryptFailed let callers branch on which stage failed via errors.Is.
  • DecryptFile writes to a temp file (mode 0600, since it holds decrypted content) in outputPath's directory and only renames onto outputPath once decryption fully succeeds.
  • A pre-existing outputPath is moved aside to a reserved backup and restored if the final rename fails, so it's never left destroyed by a failed decrypt — only a successful finalize discards it.
  • inputPath/outputPath referring to the same file (including via a hard link) is rejected up front via os.SameFile, since the final rename would otherwise replace the input while it may still be open.
  • DecryptBytes rejects plaintext over 1 GiB up front, before buffering anything — it holds the full plaintext in memory, unlike DecryptTo/DecryptFile, which stream to their destination.
  • Only os.ErrNotExist is treated as "outputPath doesn't exist" (in both the up-front checks and finalizeOutput) — any other stat error (permission, transient I/O) is a hard failure rather than silently bypassing the same-file/directory checks or the backup-before-replace safety path.

Before / after

DecryptBytes — decrypt ciphertext bytes to plaintext bytes:

// Before
reader, err := sdk.LoadTDF(bytes.NewReader(ciphertext))
if err != nil {
    return err
}
var buf bytes.Buffer
_, err = reader.WriteTo(&buf)
plaintext := buf.Bytes()

// After
plaintext, err := sdk.DecryptBytes(ctx, ciphertext)

DecryptTo — decrypt ciphertext bytes to any io.Writer:

// Before
reader, err := sdk.LoadTDF(bytes.NewReader(ciphertext))
if err != nil {
    return err
}
_, err = reader.WriteTo(os.Stdout)

// After
err := sdk.DecryptTo(ctx, os.Stdout, ciphertext)

DecryptFile — decrypt a TDF file to an output file:

// Before
in, err := os.Open("secret.tdf")
// ... LoadTDF, os.Create("secret.txt"), WriteTo, close both, handle errors at each step

// After
err := sdk.DecryptFile(ctx, "secret.tdf", "secret.txt")

Errors

plaintext, err := sdk.DecryptBytes(ctx, ciphertext)
switch {
case errors.Is(err, sdk.ErrTDFNotDecryptable):
    // Pre-decrypt: the input itself isn't decryptable. Retrying won't help.
case errors.Is(err, sdk.ErrTDFDecryptFailed):
    // Decrypt-time: most commonly a KAS rewrap failure (not entitled).
}

Every failure from all three helpers wraps one of these two sentinels — there's no third case. The underlying error (e.g. a KAS rewrap error, a payload integrity error, or the exact error a custom TDFReaderOption returned) stays reachable via errors.As through the same chain — nothing is swallowed or coerced away.

Test plan

  • go test -race -count=1 ./sdk/... — all packages pass, including 15 new tests in decrypt_test.go covering invalid ciphertext, missing input, unwritable output, output preserved/removed on load failure, same-path/hard-link rejection, directory-output rejection, TDFReaderOption error passthrough, the DecryptBytes size-limit rejection, and finalizeOutput's replace-existing-output path, each asserting errors.Is against the new sentinels where applicable — all offline, no live platform required.
  • golangci-lint run ./sdk/... — 0 new issues (11 pre-existing issues elsewhere in the package are unrelated to this change).
  • go vet ./sdk/... clean.

@marythought
marythought requested review from a team as code owners August 3, 2026 21:37
@github-actions github-actions Bot added size/m comp:sdk A software development kit, including library, for client applications and inter-service communicati labels Aug 3, 2026
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The SDK adds byte-, stream-, and file-based TDF decryption. It adds categorized errors, plaintext-size validation, temporary output handling, safe replacement, backup restoration, and cleanup reporting.

Changes

SDK decryption

Layer / File(s) Summary
Decryption APIs and error contracts
sdk/decrypterrors.go, sdk/decrypt.go, sdk/decrypt_test.go
Adds exported decryption errors, DecryptBytes, and DecryptTo. Tests cover invalid ciphertext, size limits, and reader-option errors.
Safe file decryption
sdk/decrypt.go, sdk/decrypt_test.go, sdk/README.md
Adds streamed file decryption with path checks, temporary output cleanup, replacement, backup restoration, and lifecycle tests. The README documents the new helpers and updates the quick-start example.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant DecryptFile
  participant LoadTDF
  participant TemporaryOutput
  participant FinalOutput
  Caller->>DecryptFile: provide input and output paths
  DecryptFile->>LoadTDF: load input TDF
  LoadTDF->>TemporaryOutput: write decrypted content
  DecryptFile->>FinalOutput: replace output after success
  FinalOutput-->>DecryptFile: return finalization result
  DecryptFile-->>Caller: return result or cleanup error
Loading

Suggested reviewers: dmihalcik-virtru

Poem

A rabbit decrypts bytes in a burrow bright,
Streams plaintext softly through the night.
Temp files wait while outputs stay,
Backups return if replacements stray.
Errors wear labels, cleanup runs true—
The SDK knows what decryption can do.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.41% 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
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main SDK change: adding TDF decryption convenience helpers.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/dspx-3373-decrypt-helpers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 195.448295ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 105.351478ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 711.818612ms
Throughput 140.49 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 50.228845542s
Average Latency 500.784607ms
Throughput 99.54 requests/second

@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: 1

🤖 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 `@sdk/decrypt.go`:
- Around line 171-179: Update the finalization logic around the os.Rename calls
to always pass tmpPath through joinTempFileCleanup, including when restoring
backupPath fails, and include backupPath in the returned error so callers can
locate the original file. On the successful rename path, handle
os.Remove(backupPath) errors instead of discarding them, returning the cleanup
error according to the policy documented for joinTempFileCleanup.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 5c5cbb80-b86f-43e8-a13f-8d9eba84da5d

📥 Commits

Reviewing files that changed from the base of the PR and between 2619a40 and ef1b2e1.

📒 Files selected for processing (3)
  • sdk/decrypt.go
  • sdk/decrypt_test.go
  • sdk/decrypterrors.go

Comment thread sdk/decrypt.go
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 212.198376ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 111.56618ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 415.074905ms
Throughput 240.92 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 50.301740296s
Average Latency 501.088467ms
Throughput 99.40 requests/second

@marythought
marythought marked this pull request as draft August 3, 2026 22:03
marythought added a commit that referenced this pull request Aug 4, 2026
Two gaps in finalizeOutput's tail, flagged by CodeRabbit on #3828:

- On a double rename failure (finalize fails, then backup-restore also
  fails), tmpPath was never cleaned up and the error never mentioned
  backupPath, leaving the caller with no way to find their original file.
- On the success path, a failed os.Remove(backupPath) was silently
  discarded, contradicting this file's own documented policy of surfacing
  cleanup failures via joinTempFileCleanup rather than dropping them.

Signed-off-by: Mary Dickson <mary.dickson@virtru.com>
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 214.644946ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 115.733449ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 424.961389ms
Throughput 235.32 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 52.469376766s
Average Latency 522.511566ms
Throughput 95.29 requests/second

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 326.679496ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 141.532117ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 487.102027ms
Throughput 205.30 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 48.77731183s
Average Latency 485.252099ms
Throughput 102.51 requests/second

marythought added a commit to opentdf/docs that referenced this pull request Aug 4, 2026
DecryptBytes now rejects payloads over 1 GiB before buffering, since it
holds the full plaintext in memory unlike DecryptTo/DecryptFile.

Corresponds to opentdf/platform#3828.

Signed-off-by: Mary Dickson <mary.dickson@virtru.com>
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 203.962684ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 106.462153ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 438.503772ms
Throughput 228.05 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 50.534502871s
Average Latency 503.609374ms
Throughput 98.94 requests/second

Add DecryptBytes, DecryptTo, and DecryptFile on SDK — thin wrappers over
LoadTDF + Reader.WriteTo for the common decrypt-to-plaintext case, so
callers don't need to hand-roll the load/write-to sequence themselves.

New sentinel errors ErrTDFNotDecryptable / ErrTDFDecryptFailed let callers
branch on which stage failed via errors.Is, without losing the underlying
LoadTDF/WriteTo error (reachable through the same chain via errors.As).

DecryptFile writes to a temp file (mode 0600) in outputPath's directory and
only renames onto outputPath once decryption fully succeeds. A pre-existing
outputPath is moved aside to a reserved backup and restored if the final
rename fails, so it's never left destroyed by a failed decrypt — only a
successful finalize discards it. inputPath/outputPath referring to the same
file (including via a hard link) is rejected up front via os.SameFile.

Signed-off-by: Mary Dickson <mary.dickson@virtru.com>
The Quick Start's decrypt step hand-rolled LoadTDF + os.Create + io.Copy —
exactly what DecryptTo replaces. Swap it in, and note DecryptBytes/
DecryptFile as the other two convenience shapes.

Signed-off-by: Mary Dickson <mary.dickson@virtru.com>
Two gaps in finalizeOutput's tail, flagged by CodeRabbit on #3828:

- On a double rename failure (finalize fails, then backup-restore also
  fails), tmpPath was never cleaned up and the error never mentioned
  backupPath, leaving the caller with no way to find their original file.
- On the success path, a failed os.Remove(backupPath) was silently
  discarded, contradicting this file's own documented policy of surfacing
  cleanup failures via joinTempFileCleanup rather than dropping them.

Signed-off-by: Mary Dickson <mary.dickson@virtru.com>
Matches the PR description's Before/after section — DecryptBytes was the
only one with a full example; add DecryptTo and DecryptFile too.

Signed-off-by: Mary Dickson <mary.dickson@virtru.com>
DecryptBytes buffers the full plaintext into memory, unlike DecryptTo/
DecryptFile which stream to their destination. Check the payload size
(via Seek, from the manifest's already-known segment sizes) right after
LoadTDF and before buffering anything, so an oversized payload fails fast
with ErrTDFNotDecryptable instead of silently allocating a huge buffer.

maxDecryptBytesSize is a var rather than a const so tests can override it
without constructing a multi-gigabyte fixture.

Signed-off-by: Mary Dickson <mary.dickson@virtru.com>
@marythought
marythought force-pushed the feat/dspx-3373-decrypt-helpers branch from c78b5b9 to f45d4a7 Compare August 4, 2026 16:57
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 217.378803ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 113.901839ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 426.764411ms
Throughput 234.32 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 52.655887537s
Average Latency 525.57539ms
Throughput 94.96 requests/second

@marythought
marythought marked this pull request as ready for review August 4, 2026 17:23

@pflynn-virtru pflynn-virtru left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No context.Context on any of the three new public methods, please add

@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: 2

🤖 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 `@sdk/decrypt.go`:
- Around line 112-120: Update the output-path validation in the decrypt flow
around the visible os.Stat checks and the later replacement logic: propagate any
input or output stat error unless it is explicitly os.ErrNotExist, while
preserving the same-file and directory checks for successful stats. Ensure the
replacement path only treats a missing output as absent, so permission, I/O, and
transient errors cannot lead to overwriting an existing file without backup.

In `@sdk/README.md`:
- Around line 107-119: Separate the Before and After snippets in the SDK README
into independent compilable code blocks, including the DecryptFile examples, so
declarations do not conflict across sections. Mark incomplete pseudocode as text
or provide all required setup and usage within each Go block, and remove or use
the unused in variable. Ensure the examples pass TestREADMECodeBlocks.
🪄 Autofix

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: ASSERTIVE

Plan: Pro Plus

Run ID: fa347cec-bfc4-49f8-bdfa-5f50711d5e4b

📥 Commits

Reviewing files that changed from the base of the PR and between ef1b2e1 and f45d4a7.

📒 Files selected for processing (4)
  • sdk/README.md
  • sdk/decrypt.go
  • sdk/decrypt_test.go
  • sdk/decrypterrors.go

Comment thread sdk/decrypt.go
Comment thread sdk/README.md
Per review feedback, all three new decrypt helpers now take ctx as their
first parameter. LoadTDF/Reader.WriteTo don't accept a context, so ctx is
threaded in via reader.Init(ctx) right after LoadTDF succeeds — Init does
the KAS rewrap (the only network call these methods make) under ctx, and
the subsequent WriteTo call skips its own key unwrap since the payload key
is already set.

Also fixes a stat-error handling bug flagged by CodeRabbit: DecryptFile and
finalizeOutput treated any os.Stat error on outputPath as "doesn't exist,"
not just os.ErrNotExist. A permission or transient I/O error would silently
bypass the same-file/directory checks and, in finalizeOutput, skip the
backup-before-replace safety path entirely. Now only os.ErrNotExist is
treated as absence; any other stat error is a hard failure.

sdk/README.md: split the Before/After examples into separate fenced blocks
(they previously shared one block with conflicting `:=` declarations across
Before and After) and mark DecryptFile's elided Before pseudocode as `text`
rather than `go`, since it deliberately leaves `in` unused.

Signed-off-by: Mary Dickson <mary.dickson@virtru.com>
@marythought

Copy link
Copy Markdown
Contributor Author

🤖: @pflynn-virtru Done in b028c9d — all three now take ctx context.Context as the first parameter. Since LoadTDF/Reader.WriteTo don't accept a context, ctx is threaded in via reader.Init(ctx) right after LoadTDF succeeds — Init performs the KAS rewrap (the only network call these methods make) under ctx, and the subsequent WriteTo call skips its own key unwrap since the payload key is already set.

marythought added a commit to opentdf/docs that referenced this pull request Aug 4, 2026
…ss-link

- All three Decrypt Helpers signatures/examples now take ctx context.Context
  as their first parameter, matching opentdf/platform#3828's review-driven
  signature change (ctx governs the KAS rewrap request).
- Add <SdkVersion language="go" version="0.29.0" .../> under each of the
  three Signatures, matching the established per-Signature placement
  convention used elsewhere in this file.
- Cross-link from assertion_examples.mdx's End-to-End Example to Decrypt
  Helpers, explaining why that example stays on LoadTDF directly (it reads
  tdfReader.Manifest().Assertions, which the helpers don't expose).

Signed-off-by: Mary Dickson <mary.dickson@virtru.com>
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 205.831688ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 139.162355ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 448.269136ms
Throughput 223.08 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 53.779116802s
Average Latency 535.915651ms
Throughput 92.97 requests/second

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

⚠️ Govulncheck found vulnerabilities ⚠️

The following modules have known vulnerabilities:

  • examples
  • otdfctl
  • sdk
  • service
  • lib/fixtures
  • tests-bdd

See the workflow run for details.

@marythought
marythought added this pull request to the merge queue Aug 4, 2026
marythought added a commit to opentdf/docs that referenced this pull request Aug 4, 2026
## Summary

Documents the new Go SDK `DecryptBytes`/`DecryptTo`/`DecryptFile`
convenience helpers added in
opentdf/platform#3828.

- Adds a **Decrypt Helpers** section to `docs/sdks/tdf.mdx` (between
`LoadTDF` and `IsValidTdf`): signatures, examples, `DecryptFile`'s
temp-file/rename/backup-restore behavior, and an Errors table covering
`ErrTDFNotDecryptable`/`ErrTDFDecryptFailed` with an `errors.Is`
example.
- Updates `docs/sdks/quickstart/go.mdx` — all three places that manually
did `LoadTDF` + `WriteTo` (the Step 3 running example, "Save TDF to a
File", and the "Complete Reference Implementation") now use the new
helpers.

Modeled on the existing `BulkDecrypt` section's structure (Go-only
convenience methods don't need forced `<Tabs>` shells for Java/JS).
Heading names (`DecryptBytes`/`DecryptTo`/`DecryptFile`) are unique
across the page and its imported `code_samples/*.mdx` files, per the
anchor-collision rule in `AGENTS.md`.

## Test plan

- [x] `vale docs/sdks/tdf.mdx docs/sdks/quickstart/go.mdx` — 0 errors,
warnings, or suggestions.
- [x] Every code example and behavior claim cross-checked against the
actual implementation in opentdf/platform#3828 (`sdk/decrypt.go`,
`sdk/decrypterrors.go`).
- [x] Checked for heading/anchor collisions across `tdf.mdx` and its
imports — none.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Simplified Go SDK quickstart examples for in-memory and file-based
decryption.
  * Added guidance for decrypting data to memory, writers, and files.
* Documented payload limits, streaming behavior, error classification,
and file safety protections.
* Clarified that manifest assertions are verified through the standard
TDF loading flow.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Mary Dickson <mary.dickson@virtru.com>
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 4, 2026
@marythought
marythought added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit 8c287c3 Aug 4, 2026
46 checks passed
@marythought
marythought deleted the feat/dspx-3373-decrypt-helpers branch August 4, 2026 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp:sdk A software development kit, including library, for client applications and inter-service communicati size/m

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants