Skip to content

fix: return digest from attestation state save for OCC support#2909

Merged
migmartri merged 5 commits into
chainloop-dev:mainfrom
migmartri:fix/return-digest-on-state-save
Mar 21, 2026
Merged

fix: return digest from attestation state save for OCC support#2909
migmartri merged 5 commits into
chainloop-dev:mainfrom
migmartri:fix/return-digest-on-state-save

Conversation

@migmartri

@migmartri migmartri commented Mar 21, 2026

Copy link
Copy Markdown
Member

Summary

  • Return the newly computed digest in AttestationStateServiceSaveResponse so the remote state manager can track the current server-side state
  • Fixes consecutive remote state writes failing with OCC conflict errors during attestation initialization (e.g., when multiple collectors update state sequentially)
  • Move digest computation outside the database transaction to reduce transaction hold time
go run app/cli/main.go --insecure att init --workflow foo --project bar --collectors aiconfig --replace  -y --remote-state
WRN API contacted in insecure mode
Collecting PR metadata
INF detected PR/MR context number=123 platform=github
WRN collector failed error="adding PR/MR metadata material: crafting material: PR info validation failed: jsonschema: '/url' does not validate with https://schemas.chainloop.dev/prinfo/1.1/pr-info.schema.json#/properties/url/format: '' is not valid 'uri'" collector=pr-metadata
INF discovered AI agent config files agent=claude files=7
INF uploading ai-agent-config-claude-1403254423.json - sha256:9a4432bb81d323feea47eeda5483178f7c9e58effe56458cfcd96b6070e4bc60
INF successfully collected AI agent configuration agent=claude
INF Attestation initialized! now you can check its status or add materials to it
┌───────────────────────────┬──────────────────────────────────────┐
│ Initialized At            │ 21 Mar 26 15:05 UTC                  │
├───────────────────────────┼──────────────────────────────────────┤
│ Attestation ID            │ 396bb7a3-2654-4353-ad00-d69449c0e1cb │
│ Organization              │ mgiuel                               │
│ Name                      │ foo                                  │
│ Project                   │ bar                                  │
│ Version                   │ v1.83.0+next (prerelease)            │
│ Contract                  │ bar-foo (revision 1)                 │
│ Policy violation strategy │ ADVISORY                             │
└───────────────────────────┴──────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────────────────────────┐
│ Materials                                                                             │
├─────────────┬─────────────────────────────────────────────────────────────────────────┤
│ Name        │ ai-agent-config-claude                                                  │
│ Type        │ CHAINLOOP_AI_AGENT_CONFIG                                               │
│ Set         │ Yes                                                                     │
│ Required    │ No                                                                      │
│ Value       │ ai-agent-config-claude-1403254423.json                                  │
│ Digest      │ sha256:9a4432bb81d323feea47eeda5483178f7c9e58effe56458cfcd96b6070e4bc60 │
│ Annotations │ ------                                                                  │
│             │ chainloop.material.size: 50566                                          │
│             │ chainloop.material.aiagent.name: claude                                 │
└─────────────┴─────────────────────────────────────────────────────────────────────────┘

Fixes #2908

The remote state manager was not receiving the digest of newly saved
attestation state, causing subsequent writes during the same
initialization to fail with OCC conflict errors.

Fixes chainloop-dev#2908

Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
@migmartri migmartri requested review from javirln and jiparis and removed request for javirln March 21, 2026 15:12

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 18 files

type AttestationStateRepo interface {
Initialized(ctx context.Context, workflowRunID uuid.UUID) (bool, error)
Save(ctx context.Context, workflowRunID uuid.UUID, state []byte, baseDigest string) error
Save(ctx context.Context, workflowRunID uuid.UUID, state []byte, baseDigest string) (string, error)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

this is the intersting bit

}

// Update the checksum so subsequent writes use the correct base digest
state.UpdateCheckSum = resp.GetDigest()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

and this is the key, now the local state after writing has the updated checksum so it doesn't need to load it again

Servers that don't return digests in Save responses leave the client
with a stale UpdateCheckSum. Reloading state after all collectors run
ensures the local digest matches the server, preventing OCC conflicts
on subsequent writes.

Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
Compare the digest before and after collectors run. If it changed,
writes happened and the local checksum may be stale, so reload from
the server. If unchanged, no writes occurred and reload is skipped.

Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
// Reload state if collectors modified it and the local digest may be stale.
// When the digest changed, writes happened but we can't be sure the checksum
// reflects the true server-side state (e.g. old servers don't return digests).
if c.CraftingState.UpdateCheckSum != digestBeforeCollectors {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@jiparis technically the problem could be fixed by just adding this code. But I've added a better mechanism for new servers that refresh the just updated digest.

Returning and updating the digest also fixes the problem, but only for new servers

Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
Old servers don't return digests in Save responses. Guard against
overwriting a valid checksum with an empty string, which would
break OCC protection on subsequent writes.

Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
@migmartri migmartri merged commit 08e67ad into chainloop-dev:main Mar 21, 2026
15 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.

fix: remote attestation state writes fail on second save during initialization

2 participants