diff --git a/cmd/hold/scan_backfill.go b/cmd/hold/scan_backfill.go
index 0999f2e..b577c9a 100644
--- a/cmd/hold/scan_backfill.go
+++ b/cmd/hold/scan_backfill.go
@@ -3,45 +3,32 @@ package main
import (
"context"
"fmt"
- "strings"
- "atcr.io/pkg/atproto"
"atcr.io/pkg/hold"
"github.com/spf13/cobra"
)
-// Media-type fragments that identify artifact types the scanner intentionally
-// skips. Keep this list in sync with scanner/internal/scan/worker.go's
-// unscannableConfigTypes — that map keys on config media types; here we look
-// at *layer* media types because the backfill walks the hold's layer index
-// (which has manifest AT-URIs we can join against scan records).
-//
-// Detection by layer media type is reliable: helm charts always have a single
-// layer with media type application/vnd.cncf.helm.chart.content.v1.tar+gzip;
-// in-toto / DSSE attestations use distinct layer types too.
-var unscannableLayerMediaSubstrings = []string{
- "helm.chart.content",
- "in-toto",
- "dsse.envelope",
-}
-
var scanBackfillConfigFile string
var scanBackfillCmd = &cobra.Command{
Use: "scan-backfill",
- Short: "Rewrite legacy scan records to use the status field",
+ Short: "Rewrite legacy scan records to use the status field (offline)",
Long: `Walks every io.atcr.hold.scan record on this hold and assigns a status
("skipped" or "failed") to records that pre-date the status field.
-A legacy record is one with an empty status, no SBOM blob, and zero vulnerability
-counts. The tool inspects each record's manifest's layers to decide:
+A legacy record is one with an empty status, no SBOM blob, and zero
+vulnerability counts. Layer media types decide the rewrite:
- - layer media type matches helm/in-toto/DSSE → status="skipped"
- - everything else → status="failed"
+ - helm.chart.content / in-toto / dsse.envelope → status="skipped"
+ - everything else → status="failed"
-The tool is idempotent: records that already have a status are left alone.
-Run once per hold after upgrading.`,
+The tool is idempotent and preserves each record's original scannedAt.
+
+This subcommand opens the hold's CAR store directly, so the running hold
+service must be stopped first (otherwise the embedded PDS holds an exclusive
+lock). For zero-downtime backfill on a production hold, hit the admin
+endpoint POST /admin/api/scan-backfill instead.`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := hold.LoadConfig(scanBackfillConfigFile)
@@ -56,116 +43,21 @@ Run once per hold after upgrading.`,
}
defer cleanup()
- ri := holdPDS.RecordsIndex()
- if ri == nil {
- return fmt.Errorf("records index not available")
+ logf := func(format string, args ...any) {
+ fmt.Fprintf(cmd.ErrOrStderr(), " "+format+"\n", args...)
+ }
+ res, err := holdPDS.BackfillScanStatus(ctx, logf, nil)
+ if err != nil {
+ return fmt.Errorf("backfill: %w", err)
}
- const batchSize = 200
- var (
- cursor string
- scanned int
- rewritten int
- markSkipped int
- markFailed int
- alreadyOK int
- )
-
- for {
- records, nextCursor, err := ri.ListRecords(atproto.ScanCollection, batchSize, cursor, true)
- if err != nil {
- return fmt.Errorf("list scan records: %w", err)
- }
-
- for _, rec := range records {
- scanned++
- manifestDigest := "sha256:" + rec.Rkey
-
- _, scanRecord, err := holdPDS.GetScanRecord(ctx, manifestDigest)
- if err != nil {
- fmt.Fprintf(cmd.ErrOrStderr(), " skip rkey=%s: get failed: %v\n", rec.Rkey, err)
- continue
- }
-
- // Already classified — nothing to do.
- if scanRecord.Status != "" {
- alreadyOK++
- continue
- }
-
- // Only legacy records that signal failure (nil blob + zero
- // counts) are candidates. Records with real data don't need
- // rewriting; their absent status will be treated as "ok".
- if scanRecord.SbomBlob != nil || scanRecord.Total != 0 {
- alreadyOK++
- continue
- }
-
- // Determine artifact type from layer media types.
- layers, err := holdPDS.ListLayerRecordsForManifest(ctx, scanRecord.Manifest)
- if err != nil {
- fmt.Fprintf(cmd.ErrOrStderr(), " skip rkey=%s: list layers failed: %v\n", rec.Rkey, err)
- continue
- }
-
- skipped := false
- for _, l := range layers {
- for _, frag := range unscannableLayerMediaSubstrings {
- if strings.Contains(l.MediaType, frag) {
- skipped = true
- break
- }
- }
- if skipped {
- break
- }
- }
-
- var rewrite *atproto.ScanRecord
- if skipped {
- rewrite = atproto.NewSkippedScanRecord(
- manifestDigest,
- scanRecord.Repository,
- scanRecord.UserDID,
- "backfilled: unscannable artifact type",
- scanRecord.ScannerVersion,
- )
- markSkipped++
- } else {
- rewrite = atproto.NewFailedScanRecord(
- manifestDigest,
- scanRecord.Repository,
- scanRecord.UserDID,
- "backfilled: legacy record (no SBOM and zero counts)",
- scanRecord.ScannerVersion,
- )
- markFailed++
- }
- // Preserve the original ScannedAt — rewriting it would either
- // reset the rescan timer or invalidate audit signals.
- if scanRecord.ScannedAt != "" {
- rewrite.ScannedAt = scanRecord.ScannedAt
- }
-
- if _, _, err := holdPDS.CreateScanRecord(ctx, rewrite); err != nil {
- fmt.Fprintf(cmd.ErrOrStderr(), " rewrite rkey=%s failed: %v\n", rec.Rkey, err)
- continue
- }
- rewritten++
- }
-
- if nextCursor == "" || len(records) == 0 {
- break
- }
- cursor = nextCursor
- }
-
- fmt.Fprintf(cmd.OutOrStdout(), "Backfill complete:\n")
- fmt.Fprintf(cmd.OutOrStdout(), " scanned: %d\n", scanned)
- fmt.Fprintf(cmd.OutOrStdout(), " already-tagged: %d\n", alreadyOK)
- fmt.Fprintf(cmd.OutOrStdout(), " → skipped: %d\n", markSkipped)
- fmt.Fprintf(cmd.OutOrStdout(), " → failed: %d\n", markFailed)
- fmt.Fprintf(cmd.OutOrStdout(), " rewritten: %d\n", rewritten)
+ out := cmd.OutOrStdout()
+ fmt.Fprintf(out, "Backfill complete:\n")
+ fmt.Fprintf(out, " scanned: %d\n", res.Scanned)
+ fmt.Fprintf(out, " already-tagged: %d\n", res.AlreadyTagged)
+ fmt.Fprintf(out, " → skipped: %d\n", res.MarkedSkipped)
+ fmt.Fprintf(out, " → failed: %d\n", res.MarkedFailed)
+ fmt.Fprintf(out, " rewritten: %d\n", res.Rewritten)
return nil
},
}
diff --git a/lexicons/io/atcr/hold/image/config.json b/lexicons/io/atcr/hold/image/config.json
index bfaf8e7..fbe739e 100644
--- a/lexicons/io/atcr/hold/image/config.json
+++ b/lexicons/io/atcr/hold/image/config.json
@@ -18,7 +18,7 @@
"configJson": {
"type": "string",
"description": "Raw OCI image config JSON blob",
- "maxLength": 65536
+ "maxLength": 1000000
},
"createdAt": {
"type": "string",
diff --git a/pkg/appview/templates/partials/image-advisor-results.html b/pkg/appview/templates/partials/image-advisor-results.html
index fdab1b5..cc7f500 100644
--- a/pkg/appview/templates/partials/image-advisor-results.html
+++ b/pkg/appview/templates/partials/image-advisor-results.html
@@ -2,7 +2,7 @@
{{ if eq .Error "upgrade_required" }}
diff --git a/pkg/atproto/cbor_gen.go b/pkg/atproto/cbor_gen.go
index 9e00218..ba7fd14 100644
--- a/pkg/atproto/cbor_gen.go
+++ b/pkg/atproto/cbor_gen.go
@@ -37,7 +37,7 @@ func (t *CrewRecord) MarshalCBOR(w io.Writer) error {
}
// t.Role (string) (string)
- if len("role") > 8192 {
+ if len("role") > 1000000 {
return xerrors.Errorf("Value in field \"role\" was too long")
}
@@ -48,7 +48,7 @@ func (t *CrewRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Role) > 8192 {
+ if len(t.Role) > 1000000 {
return xerrors.Errorf("Value in field t.Role was too long")
}
@@ -62,7 +62,7 @@ func (t *CrewRecord) MarshalCBOR(w io.Writer) error {
// t.Tier (string) (string)
if t.Tier != "" {
- if len("tier") > 8192 {
+ if len("tier") > 1000000 {
return xerrors.Errorf("Value in field \"tier\" was too long")
}
@@ -73,7 +73,7 @@ func (t *CrewRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Tier) > 8192 {
+ if len(t.Tier) > 1000000 {
return xerrors.Errorf("Value in field t.Tier was too long")
}
@@ -86,7 +86,7 @@ func (t *CrewRecord) MarshalCBOR(w io.Writer) error {
}
// t.Type (string) (string)
- if len("$type") > 8192 {
+ if len("$type") > 1000000 {
return xerrors.Errorf("Value in field \"$type\" was too long")
}
@@ -97,7 +97,7 @@ func (t *CrewRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Type) > 8192 {
+ if len(t.Type) > 1000000 {
return xerrors.Errorf("Value in field t.Type was too long")
}
@@ -109,7 +109,7 @@ func (t *CrewRecord) MarshalCBOR(w io.Writer) error {
}
// t.Member (string) (string)
- if len("member") > 8192 {
+ if len("member") > 1000000 {
return xerrors.Errorf("Value in field \"member\" was too long")
}
@@ -120,7 +120,7 @@ func (t *CrewRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Member) > 8192 {
+ if len(t.Member) > 1000000 {
return xerrors.Errorf("Value in field t.Member was too long")
}
@@ -132,7 +132,7 @@ func (t *CrewRecord) MarshalCBOR(w io.Writer) error {
}
// t.AddedAt (string) (string)
- if len("addedAt") > 8192 {
+ if len("addedAt") > 1000000 {
return xerrors.Errorf("Value in field \"addedAt\" was too long")
}
@@ -143,7 +143,7 @@ func (t *CrewRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.AddedAt) > 8192 {
+ if len(t.AddedAt) > 1000000 {
return xerrors.Errorf("Value in field t.AddedAt was too long")
}
@@ -155,7 +155,7 @@ func (t *CrewRecord) MarshalCBOR(w io.Writer) error {
}
// t.Plankowner (bool) (bool)
- if len("plankowner") > 8192 {
+ if len("plankowner") > 1000000 {
return xerrors.Errorf("Value in field \"plankowner\" was too long")
}
@@ -171,7 +171,7 @@ func (t *CrewRecord) MarshalCBOR(w io.Writer) error {
}
// t.Permissions ([]string) (slice)
- if len("permissions") > 8192 {
+ if len("permissions") > 1000000 {
return xerrors.Errorf("Value in field \"permissions\" was too long")
}
@@ -190,7 +190,7 @@ func (t *CrewRecord) MarshalCBOR(w io.Writer) error {
return err
}
for _, v := range t.Permissions {
- if len(v) > 8192 {
+ if len(v) > 1000000 {
return xerrors.Errorf("Value in field v was too long")
}
@@ -232,7 +232,7 @@ func (t *CrewRecord) UnmarshalCBOR(r io.Reader) (err error) {
nameBuf := make([]byte, 11)
for i := uint64(0); i < n; i++ {
- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
if err != nil {
return err
}
@@ -250,7 +250,7 @@ func (t *CrewRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "role":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -261,7 +261,7 @@ func (t *CrewRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "tier":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -272,7 +272,7 @@ func (t *CrewRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "$type":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -283,7 +283,7 @@ func (t *CrewRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "member":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -294,7 +294,7 @@ func (t *CrewRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "addedAt":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -349,7 +349,7 @@ func (t *CrewRecord) UnmarshalCBOR(r io.Reader) (err error) {
_ = err
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -392,7 +392,7 @@ func (t *CaptainRecord) MarshalCBOR(w io.Writer) error {
}
// t.Type (string) (string)
- if len("$type") > 8192 {
+ if len("$type") > 1000000 {
return xerrors.Errorf("Value in field \"$type\" was too long")
}
@@ -403,7 +403,7 @@ func (t *CaptainRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Type) > 8192 {
+ if len(t.Type) > 1000000 {
return xerrors.Errorf("Value in field t.Type was too long")
}
@@ -415,7 +415,7 @@ func (t *CaptainRecord) MarshalCBOR(w io.Writer) error {
}
// t.Owner (string) (string)
- if len("owner") > 8192 {
+ if len("owner") > 1000000 {
return xerrors.Errorf("Value in field \"owner\" was too long")
}
@@ -426,7 +426,7 @@ func (t *CaptainRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Owner) > 8192 {
+ if len(t.Owner) > 1000000 {
return xerrors.Errorf("Value in field t.Owner was too long")
}
@@ -438,7 +438,7 @@ func (t *CaptainRecord) MarshalCBOR(w io.Writer) error {
}
// t.Public (bool) (bool)
- if len("public") > 8192 {
+ if len("public") > 1000000 {
return xerrors.Errorf("Value in field \"public\" was too long")
}
@@ -456,7 +456,7 @@ func (t *CaptainRecord) MarshalCBOR(w io.Writer) error {
// t.Region (string) (string)
if t.Region != "" {
- if len("region") > 8192 {
+ if len("region") > 1000000 {
return xerrors.Errorf("Value in field \"region\" was too long")
}
@@ -467,7 +467,7 @@ func (t *CaptainRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Region) > 8192 {
+ if len(t.Region) > 1000000 {
return xerrors.Errorf("Value in field t.Region was too long")
}
@@ -482,7 +482,7 @@ func (t *CaptainRecord) MarshalCBOR(w io.Writer) error {
// t.Successor (string) (string)
if t.Successor != "" {
- if len("successor") > 8192 {
+ if len("successor") > 1000000 {
return xerrors.Errorf("Value in field \"successor\" was too long")
}
@@ -493,7 +493,7 @@ func (t *CaptainRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Successor) > 8192 {
+ if len(t.Successor) > 1000000 {
return xerrors.Errorf("Value in field t.Successor was too long")
}
@@ -506,7 +506,7 @@ func (t *CaptainRecord) MarshalCBOR(w io.Writer) error {
}
// t.DeployedAt (string) (string)
- if len("deployedAt") > 8192 {
+ if len("deployedAt") > 1000000 {
return xerrors.Errorf("Value in field \"deployedAt\" was too long")
}
@@ -517,7 +517,7 @@ func (t *CaptainRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.DeployedAt) > 8192 {
+ if len(t.DeployedAt) > 1000000 {
return xerrors.Errorf("Value in field t.DeployedAt was too long")
}
@@ -529,7 +529,7 @@ func (t *CaptainRecord) MarshalCBOR(w io.Writer) error {
}
// t.AllowAllCrew (bool) (bool)
- if len("allowAllCrew") > 8192 {
+ if len("allowAllCrew") > 1000000 {
return xerrors.Errorf("Value in field \"allowAllCrew\" was too long")
}
@@ -545,7 +545,7 @@ func (t *CaptainRecord) MarshalCBOR(w io.Writer) error {
}
// t.EnableBlueskyPosts (bool) (bool)
- if len("enableBlueskyPosts") > 8192 {
+ if len("enableBlueskyPosts") > 1000000 {
return xerrors.Errorf("Value in field \"enableBlueskyPosts\" was too long")
}
@@ -589,7 +589,7 @@ func (t *CaptainRecord) UnmarshalCBOR(r io.Reader) (err error) {
nameBuf := make([]byte, 18)
for i := uint64(0); i < n; i++ {
- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
if err != nil {
return err
}
@@ -607,7 +607,7 @@ func (t *CaptainRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "$type":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -618,7 +618,7 @@ func (t *CaptainRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "owner":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -647,7 +647,7 @@ func (t *CaptainRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "region":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -658,7 +658,7 @@ func (t *CaptainRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "successor":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -669,7 +669,7 @@ func (t *CaptainRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "deployedAt":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -736,7 +736,7 @@ func (t *LayerRecord) MarshalCBOR(w io.Writer) error {
}
// t.Size (int64) (int64)
- if len("size") > 8192 {
+ if len("size") > 1000000 {
return xerrors.Errorf("Value in field \"size\" was too long")
}
@@ -758,7 +758,7 @@ func (t *LayerRecord) MarshalCBOR(w io.Writer) error {
}
// t.Type (string) (string)
- if len("$type") > 8192 {
+ if len("$type") > 1000000 {
return xerrors.Errorf("Value in field \"$type\" was too long")
}
@@ -769,7 +769,7 @@ func (t *LayerRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Type) > 8192 {
+ if len(t.Type) > 1000000 {
return xerrors.Errorf("Value in field t.Type was too long")
}
@@ -781,7 +781,7 @@ func (t *LayerRecord) MarshalCBOR(w io.Writer) error {
}
// t.Digest (string) (string)
- if len("digest") > 8192 {
+ if len("digest") > 1000000 {
return xerrors.Errorf("Value in field \"digest\" was too long")
}
@@ -792,7 +792,7 @@ func (t *LayerRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Digest) > 8192 {
+ if len(t.Digest) > 1000000 {
return xerrors.Errorf("Value in field t.Digest was too long")
}
@@ -804,7 +804,7 @@ func (t *LayerRecord) MarshalCBOR(w io.Writer) error {
}
// t.UserDID (string) (string)
- if len("userDid") > 8192 {
+ if len("userDid") > 1000000 {
return xerrors.Errorf("Value in field \"userDid\" was too long")
}
@@ -815,7 +815,7 @@ func (t *LayerRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.UserDID) > 8192 {
+ if len(t.UserDID) > 1000000 {
return xerrors.Errorf("Value in field t.UserDID was too long")
}
@@ -827,7 +827,7 @@ func (t *LayerRecord) MarshalCBOR(w io.Writer) error {
}
// t.Manifest (string) (string)
- if len("manifest") > 8192 {
+ if len("manifest") > 1000000 {
return xerrors.Errorf("Value in field \"manifest\" was too long")
}
@@ -838,7 +838,7 @@ func (t *LayerRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Manifest) > 8192 {
+ if len(t.Manifest) > 1000000 {
return xerrors.Errorf("Value in field t.Manifest was too long")
}
@@ -850,7 +850,7 @@ func (t *LayerRecord) MarshalCBOR(w io.Writer) error {
}
// t.CreatedAt (string) (string)
- if len("createdAt") > 8192 {
+ if len("createdAt") > 1000000 {
return xerrors.Errorf("Value in field \"createdAt\" was too long")
}
@@ -861,7 +861,7 @@ func (t *LayerRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.CreatedAt) > 8192 {
+ if len(t.CreatedAt) > 1000000 {
return xerrors.Errorf("Value in field t.CreatedAt was too long")
}
@@ -873,7 +873,7 @@ func (t *LayerRecord) MarshalCBOR(w io.Writer) error {
}
// t.MediaType (string) (string)
- if len("mediaType") > 8192 {
+ if len("mediaType") > 1000000 {
return xerrors.Errorf("Value in field \"mediaType\" was too long")
}
@@ -884,7 +884,7 @@ func (t *LayerRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.MediaType) > 8192 {
+ if len(t.MediaType) > 1000000 {
return xerrors.Errorf("Value in field t.MediaType was too long")
}
@@ -924,7 +924,7 @@ func (t *LayerRecord) UnmarshalCBOR(r io.Reader) (err error) {
nameBuf := make([]byte, 9)
for i := uint64(0); i < n; i++ {
- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
if err != nil {
return err
}
@@ -968,7 +968,7 @@ func (t *LayerRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "$type":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -979,7 +979,7 @@ func (t *LayerRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "digest":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -990,7 +990,7 @@ func (t *LayerRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "userDid":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1001,7 +1001,7 @@ func (t *LayerRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "manifest":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1012,7 +1012,7 @@ func (t *LayerRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "createdAt":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1023,7 +1023,7 @@ func (t *LayerRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "mediaType":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1054,7 +1054,7 @@ func (t *TangledProfileRecord) MarshalCBOR(w io.Writer) error {
}
// t.Type (string) (string)
- if len("$type") > 8192 {
+ if len("$type") > 1000000 {
return xerrors.Errorf("Value in field \"$type\" was too long")
}
@@ -1065,7 +1065,7 @@ func (t *TangledProfileRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Type) > 8192 {
+ if len(t.Type) > 1000000 {
return xerrors.Errorf("Value in field t.Type was too long")
}
@@ -1077,7 +1077,7 @@ func (t *TangledProfileRecord) MarshalCBOR(w io.Writer) error {
}
// t.Links ([]string) (slice)
- if len("links") > 8192 {
+ if len("links") > 1000000 {
return xerrors.Errorf("Value in field \"links\" was too long")
}
@@ -1096,7 +1096,7 @@ func (t *TangledProfileRecord) MarshalCBOR(w io.Writer) error {
return err
}
for _, v := range t.Links {
- if len(v) > 8192 {
+ if len(v) > 1000000 {
return xerrors.Errorf("Value in field v was too long")
}
@@ -1110,7 +1110,7 @@ func (t *TangledProfileRecord) MarshalCBOR(w io.Writer) error {
}
// t.Stats ([]string) (slice)
- if len("stats") > 8192 {
+ if len("stats") > 1000000 {
return xerrors.Errorf("Value in field \"stats\" was too long")
}
@@ -1129,7 +1129,7 @@ func (t *TangledProfileRecord) MarshalCBOR(w io.Writer) error {
return err
}
for _, v := range t.Stats {
- if len(v) > 8192 {
+ if len(v) > 1000000 {
return xerrors.Errorf("Value in field v was too long")
}
@@ -1143,7 +1143,7 @@ func (t *TangledProfileRecord) MarshalCBOR(w io.Writer) error {
}
// t.Bluesky (bool) (bool)
- if len("bluesky") > 8192 {
+ if len("bluesky") > 1000000 {
return xerrors.Errorf("Value in field \"bluesky\" was too long")
}
@@ -1159,7 +1159,7 @@ func (t *TangledProfileRecord) MarshalCBOR(w io.Writer) error {
}
// t.Location (string) (string)
- if len("location") > 8192 {
+ if len("location") > 1000000 {
return xerrors.Errorf("Value in field \"location\" was too long")
}
@@ -1170,7 +1170,7 @@ func (t *TangledProfileRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Location) > 8192 {
+ if len(t.Location) > 1000000 {
return xerrors.Errorf("Value in field t.Location was too long")
}
@@ -1182,7 +1182,7 @@ func (t *TangledProfileRecord) MarshalCBOR(w io.Writer) error {
}
// t.Description (string) (string)
- if len("description") > 8192 {
+ if len("description") > 1000000 {
return xerrors.Errorf("Value in field \"description\" was too long")
}
@@ -1193,7 +1193,7 @@ func (t *TangledProfileRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Description) > 8192 {
+ if len(t.Description) > 1000000 {
return xerrors.Errorf("Value in field t.Description was too long")
}
@@ -1205,7 +1205,7 @@ func (t *TangledProfileRecord) MarshalCBOR(w io.Writer) error {
}
// t.PinnedRepositories ([]string) (slice)
- if len("pinnedRepositories") > 8192 {
+ if len("pinnedRepositories") > 1000000 {
return xerrors.Errorf("Value in field \"pinnedRepositories\" was too long")
}
@@ -1224,7 +1224,7 @@ func (t *TangledProfileRecord) MarshalCBOR(w io.Writer) error {
return err
}
for _, v := range t.PinnedRepositories {
- if len(v) > 8192 {
+ if len(v) > 1000000 {
return xerrors.Errorf("Value in field v was too long")
}
@@ -1266,7 +1266,7 @@ func (t *TangledProfileRecord) UnmarshalCBOR(r io.Reader) (err error) {
nameBuf := make([]byte, 18)
for i := uint64(0); i < n; i++ {
- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
if err != nil {
return err
}
@@ -1284,7 +1284,7 @@ func (t *TangledProfileRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "$type":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1321,7 +1321,7 @@ func (t *TangledProfileRecord) UnmarshalCBOR(r io.Reader) (err error) {
_ = err
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1361,7 +1361,7 @@ func (t *TangledProfileRecord) UnmarshalCBOR(r io.Reader) (err error) {
_ = err
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1393,7 +1393,7 @@ func (t *TangledProfileRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "location":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1404,7 +1404,7 @@ func (t *TangledProfileRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "description":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1441,7 +1441,7 @@ func (t *TangledProfileRecord) UnmarshalCBOR(r io.Reader) (err error) {
_ = err
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1484,7 +1484,7 @@ func (t *StatsRecord) MarshalCBOR(w io.Writer) error {
}
// t.Type (string) (string)
- if len("$type") > 8192 {
+ if len("$type") > 1000000 {
return xerrors.Errorf("Value in field \"$type\" was too long")
}
@@ -1495,7 +1495,7 @@ func (t *StatsRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Type) > 8192 {
+ if len(t.Type) > 1000000 {
return xerrors.Errorf("Value in field t.Type was too long")
}
@@ -1509,7 +1509,7 @@ func (t *StatsRecord) MarshalCBOR(w io.Writer) error {
// t.LastPull (string) (string)
if t.LastPull != "" {
- if len("lastPull") > 8192 {
+ if len("lastPull") > 1000000 {
return xerrors.Errorf("Value in field \"lastPull\" was too long")
}
@@ -1520,7 +1520,7 @@ func (t *StatsRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.LastPull) > 8192 {
+ if len(t.LastPull) > 1000000 {
return xerrors.Errorf("Value in field t.LastPull was too long")
}
@@ -1535,7 +1535,7 @@ func (t *StatsRecord) MarshalCBOR(w io.Writer) error {
// t.LastPush (string) (string)
if t.LastPush != "" {
- if len("lastPush") > 8192 {
+ if len("lastPush") > 1000000 {
return xerrors.Errorf("Value in field \"lastPush\" was too long")
}
@@ -1546,7 +1546,7 @@ func (t *StatsRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.LastPush) > 8192 {
+ if len(t.LastPush) > 1000000 {
return xerrors.Errorf("Value in field t.LastPush was too long")
}
@@ -1559,7 +1559,7 @@ func (t *StatsRecord) MarshalCBOR(w io.Writer) error {
}
// t.OwnerDID (string) (string)
- if len("ownerDid") > 8192 {
+ if len("ownerDid") > 1000000 {
return xerrors.Errorf("Value in field \"ownerDid\" was too long")
}
@@ -1570,7 +1570,7 @@ func (t *StatsRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.OwnerDID) > 8192 {
+ if len(t.OwnerDID) > 1000000 {
return xerrors.Errorf("Value in field t.OwnerDID was too long")
}
@@ -1582,7 +1582,7 @@ func (t *StatsRecord) MarshalCBOR(w io.Writer) error {
}
// t.PullCount (int64) (int64)
- if len("pullCount") > 8192 {
+ if len("pullCount") > 1000000 {
return xerrors.Errorf("Value in field \"pullCount\" was too long")
}
@@ -1604,7 +1604,7 @@ func (t *StatsRecord) MarshalCBOR(w io.Writer) error {
}
// t.PushCount (int64) (int64)
- if len("pushCount") > 8192 {
+ if len("pushCount") > 1000000 {
return xerrors.Errorf("Value in field \"pushCount\" was too long")
}
@@ -1626,7 +1626,7 @@ func (t *StatsRecord) MarshalCBOR(w io.Writer) error {
}
// t.UpdatedAt (string) (string)
- if len("updatedAt") > 8192 {
+ if len("updatedAt") > 1000000 {
return xerrors.Errorf("Value in field \"updatedAt\" was too long")
}
@@ -1637,7 +1637,7 @@ func (t *StatsRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.UpdatedAt) > 8192 {
+ if len(t.UpdatedAt) > 1000000 {
return xerrors.Errorf("Value in field t.UpdatedAt was too long")
}
@@ -1649,7 +1649,7 @@ func (t *StatsRecord) MarshalCBOR(w io.Writer) error {
}
// t.Repository (string) (string)
- if len("repository") > 8192 {
+ if len("repository") > 1000000 {
return xerrors.Errorf("Value in field \"repository\" was too long")
}
@@ -1660,7 +1660,7 @@ func (t *StatsRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Repository) > 8192 {
+ if len(t.Repository) > 1000000 {
return xerrors.Errorf("Value in field t.Repository was too long")
}
@@ -1700,7 +1700,7 @@ func (t *StatsRecord) UnmarshalCBOR(r io.Reader) (err error) {
nameBuf := make([]byte, 10)
for i := uint64(0); i < n; i++ {
- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
if err != nil {
return err
}
@@ -1718,7 +1718,7 @@ func (t *StatsRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "$type":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1729,7 +1729,7 @@ func (t *StatsRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "lastPull":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1740,7 +1740,7 @@ func (t *StatsRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "lastPush":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1751,7 +1751,7 @@ func (t *StatsRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "ownerDid":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1814,7 +1814,7 @@ func (t *StatsRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "updatedAt":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1825,7 +1825,7 @@ func (t *StatsRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "repository":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -1856,7 +1856,7 @@ func (t *DailyStatsRecord) MarshalCBOR(w io.Writer) error {
}
// t.Date (string) (string)
- if len("date") > 8192 {
+ if len("date") > 1000000 {
return xerrors.Errorf("Value in field \"date\" was too long")
}
@@ -1867,7 +1867,7 @@ func (t *DailyStatsRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Date) > 8192 {
+ if len(t.Date) > 1000000 {
return xerrors.Errorf("Value in field t.Date was too long")
}
@@ -1879,7 +1879,7 @@ func (t *DailyStatsRecord) MarshalCBOR(w io.Writer) error {
}
// t.Type (string) (string)
- if len("$type") > 8192 {
+ if len("$type") > 1000000 {
return xerrors.Errorf("Value in field \"$type\" was too long")
}
@@ -1890,7 +1890,7 @@ func (t *DailyStatsRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Type) > 8192 {
+ if len(t.Type) > 1000000 {
return xerrors.Errorf("Value in field t.Type was too long")
}
@@ -1902,7 +1902,7 @@ func (t *DailyStatsRecord) MarshalCBOR(w io.Writer) error {
}
// t.OwnerDID (string) (string)
- if len("ownerDid") > 8192 {
+ if len("ownerDid") > 1000000 {
return xerrors.Errorf("Value in field \"ownerDid\" was too long")
}
@@ -1913,7 +1913,7 @@ func (t *DailyStatsRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.OwnerDID) > 8192 {
+ if len(t.OwnerDID) > 1000000 {
return xerrors.Errorf("Value in field t.OwnerDID was too long")
}
@@ -1925,7 +1925,7 @@ func (t *DailyStatsRecord) MarshalCBOR(w io.Writer) error {
}
// t.PullCount (int64) (int64)
- if len("pullCount") > 8192 {
+ if len("pullCount") > 1000000 {
return xerrors.Errorf("Value in field \"pullCount\" was too long")
}
@@ -1947,7 +1947,7 @@ func (t *DailyStatsRecord) MarshalCBOR(w io.Writer) error {
}
// t.PushCount (int64) (int64)
- if len("pushCount") > 8192 {
+ if len("pushCount") > 1000000 {
return xerrors.Errorf("Value in field \"pushCount\" was too long")
}
@@ -1969,7 +1969,7 @@ func (t *DailyStatsRecord) MarshalCBOR(w io.Writer) error {
}
// t.UpdatedAt (string) (string)
- if len("updatedAt") > 8192 {
+ if len("updatedAt") > 1000000 {
return xerrors.Errorf("Value in field \"updatedAt\" was too long")
}
@@ -1980,7 +1980,7 @@ func (t *DailyStatsRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.UpdatedAt) > 8192 {
+ if len(t.UpdatedAt) > 1000000 {
return xerrors.Errorf("Value in field t.UpdatedAt was too long")
}
@@ -1992,7 +1992,7 @@ func (t *DailyStatsRecord) MarshalCBOR(w io.Writer) error {
}
// t.Repository (string) (string)
- if len("repository") > 8192 {
+ if len("repository") > 1000000 {
return xerrors.Errorf("Value in field \"repository\" was too long")
}
@@ -2003,7 +2003,7 @@ func (t *DailyStatsRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Repository) > 8192 {
+ if len(t.Repository) > 1000000 {
return xerrors.Errorf("Value in field t.Repository was too long")
}
@@ -2043,7 +2043,7 @@ func (t *DailyStatsRecord) UnmarshalCBOR(r io.Reader) (err error) {
nameBuf := make([]byte, 10)
for i := uint64(0); i < n; i++ {
- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
if err != nil {
return err
}
@@ -2061,7 +2061,7 @@ func (t *DailyStatsRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "date":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -2072,7 +2072,7 @@ func (t *DailyStatsRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "$type":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -2083,7 +2083,7 @@ func (t *DailyStatsRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "ownerDid":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -2146,7 +2146,7 @@ func (t *DailyStatsRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "updatedAt":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -2157,7 +2157,7 @@ func (t *DailyStatsRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "repository":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -2197,7 +2197,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
}
// t.Low (int64) (int64)
- if len("low") > 8192 {
+ if len("low") > 1000000 {
return xerrors.Errorf("Value in field \"low\" was too long")
}
@@ -2219,7 +2219,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
}
// t.High (int64) (int64)
- if len("high") > 8192 {
+ if len("high") > 1000000 {
return xerrors.Errorf("Value in field \"high\" was too long")
}
@@ -2241,7 +2241,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
}
// t.Type (string) (string)
- if len("$type") > 8192 {
+ if len("$type") > 1000000 {
return xerrors.Errorf("Value in field \"$type\" was too long")
}
@@ -2252,7 +2252,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Type) > 8192 {
+ if len(t.Type) > 1000000 {
return xerrors.Errorf("Value in field t.Type was too long")
}
@@ -2264,7 +2264,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
}
// t.Total (int64) (int64)
- if len("total") > 8192 {
+ if len("total") > 1000000 {
return xerrors.Errorf("Value in field \"total\" was too long")
}
@@ -2286,7 +2286,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
}
// t.Medium (int64) (int64)
- if len("medium") > 8192 {
+ if len("medium") > 1000000 {
return xerrors.Errorf("Value in field \"medium\" was too long")
}
@@ -2310,7 +2310,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
// t.Reason (string) (string)
if t.Reason != "" {
- if len("reason") > 8192 {
+ if len("reason") > 1000000 {
return xerrors.Errorf("Value in field \"reason\" was too long")
}
@@ -2321,7 +2321,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Reason) > 8192 {
+ if len(t.Reason) > 1000000 {
return xerrors.Errorf("Value in field t.Reason was too long")
}
@@ -2336,7 +2336,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
// t.Status (string) (string)
if t.Status != "" {
- if len("status") > 8192 {
+ if len("status") > 1000000 {
return xerrors.Errorf("Value in field \"status\" was too long")
}
@@ -2347,7 +2347,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Status) > 8192 {
+ if len(t.Status) > 1000000 {
return xerrors.Errorf("Value in field t.Status was too long")
}
@@ -2360,7 +2360,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
}
// t.UserDID (string) (string)
- if len("userDid") > 8192 {
+ if len("userDid") > 1000000 {
return xerrors.Errorf("Value in field \"userDid\" was too long")
}
@@ -2371,7 +2371,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.UserDID) > 8192 {
+ if len(t.UserDID) > 1000000 {
return xerrors.Errorf("Value in field t.UserDID was too long")
}
@@ -2383,7 +2383,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
}
// t.Critical (int64) (int64)
- if len("critical") > 8192 {
+ if len("critical") > 1000000 {
return xerrors.Errorf("Value in field \"critical\" was too long")
}
@@ -2405,7 +2405,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
}
// t.Manifest (string) (string)
- if len("manifest") > 8192 {
+ if len("manifest") > 1000000 {
return xerrors.Errorf("Value in field \"manifest\" was too long")
}
@@ -2416,7 +2416,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Manifest) > 8192 {
+ if len(t.Manifest) > 1000000 {
return xerrors.Errorf("Value in field t.Manifest was too long")
}
@@ -2428,7 +2428,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
}
// t.SbomBlob (util.LexBlob) (struct)
- if len("sbomBlob") > 8192 {
+ if len("sbomBlob") > 1000000 {
return xerrors.Errorf("Value in field \"sbomBlob\" was too long")
}
@@ -2444,7 +2444,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
}
// t.ScannedAt (string) (string)
- if len("scannedAt") > 8192 {
+ if len("scannedAt") > 1000000 {
return xerrors.Errorf("Value in field \"scannedAt\" was too long")
}
@@ -2455,7 +2455,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.ScannedAt) > 8192 {
+ if len(t.ScannedAt) > 1000000 {
return xerrors.Errorf("Value in field t.ScannedAt was too long")
}
@@ -2467,7 +2467,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
}
// t.Repository (string) (string)
- if len("repository") > 8192 {
+ if len("repository") > 1000000 {
return xerrors.Errorf("Value in field \"repository\" was too long")
}
@@ -2478,7 +2478,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Repository) > 8192 {
+ if len(t.Repository) > 1000000 {
return xerrors.Errorf("Value in field t.Repository was too long")
}
@@ -2490,7 +2490,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
}
// t.ScannerVersion (string) (string)
- if len("scannerVersion") > 8192 {
+ if len("scannerVersion") > 1000000 {
return xerrors.Errorf("Value in field \"scannerVersion\" was too long")
}
@@ -2501,7 +2501,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.ScannerVersion) > 8192 {
+ if len(t.ScannerVersion) > 1000000 {
return xerrors.Errorf("Value in field t.ScannerVersion was too long")
}
@@ -2513,7 +2513,7 @@ func (t *ScanRecord) MarshalCBOR(w io.Writer) error {
}
// t.VulnReportBlob (util.LexBlob) (struct)
- if len("vulnReportBlob") > 8192 {
+ if len("vulnReportBlob") > 1000000 {
return xerrors.Errorf("Value in field \"vulnReportBlob\" was too long")
}
@@ -2557,7 +2557,7 @@ func (t *ScanRecord) UnmarshalCBOR(r io.Reader) (err error) {
nameBuf := make([]byte, 14)
for i := uint64(0); i < n; i++ {
- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
if err != nil {
return err
}
@@ -2627,7 +2627,7 @@ func (t *ScanRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "$type":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -2690,7 +2690,7 @@ func (t *ScanRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "reason":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -2701,7 +2701,7 @@ func (t *ScanRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "status":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -2712,7 +2712,7 @@ func (t *ScanRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "userDid":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -2749,7 +2749,7 @@ func (t *ScanRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "manifest":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -2780,7 +2780,7 @@ func (t *ScanRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "scannedAt":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -2791,7 +2791,7 @@ func (t *ScanRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "repository":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -2802,7 +2802,7 @@ func (t *ScanRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "scannerVersion":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -2853,7 +2853,7 @@ func (t *ImageConfigRecord) MarshalCBOR(w io.Writer) error {
}
// t.Type (string) (string)
- if len("$type") > 8192 {
+ if len("$type") > 1000000 {
return xerrors.Errorf("Value in field \"$type\" was too long")
}
@@ -2864,7 +2864,7 @@ func (t *ImageConfigRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Type) > 8192 {
+ if len(t.Type) > 1000000 {
return xerrors.Errorf("Value in field t.Type was too long")
}
@@ -2876,7 +2876,7 @@ func (t *ImageConfigRecord) MarshalCBOR(w io.Writer) error {
}
// t.Manifest (string) (string)
- if len("manifest") > 8192 {
+ if len("manifest") > 1000000 {
return xerrors.Errorf("Value in field \"manifest\" was too long")
}
@@ -2887,7 +2887,7 @@ func (t *ImageConfigRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.Manifest) > 8192 {
+ if len(t.Manifest) > 1000000 {
return xerrors.Errorf("Value in field t.Manifest was too long")
}
@@ -2899,7 +2899,7 @@ func (t *ImageConfigRecord) MarshalCBOR(w io.Writer) error {
}
// t.CreatedAt (string) (string)
- if len("createdAt") > 8192 {
+ if len("createdAt") > 1000000 {
return xerrors.Errorf("Value in field \"createdAt\" was too long")
}
@@ -2910,7 +2910,7 @@ func (t *ImageConfigRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.CreatedAt) > 8192 {
+ if len(t.CreatedAt) > 1000000 {
return xerrors.Errorf("Value in field t.CreatedAt was too long")
}
@@ -2922,7 +2922,7 @@ func (t *ImageConfigRecord) MarshalCBOR(w io.Writer) error {
}
// t.ConfigJSON (string) (string)
- if len("configJson") > 8192 {
+ if len("configJson") > 1000000 {
return xerrors.Errorf("Value in field \"configJson\" was too long")
}
@@ -2933,7 +2933,7 @@ func (t *ImageConfigRecord) MarshalCBOR(w io.Writer) error {
return err
}
- if len(t.ConfigJSON) > 8192 {
+ if len(t.ConfigJSON) > 1000000 {
return xerrors.Errorf("Value in field t.ConfigJSON was too long")
}
@@ -2973,7 +2973,7 @@ func (t *ImageConfigRecord) UnmarshalCBOR(r io.Reader) (err error) {
nameBuf := make([]byte, 10)
for i := uint64(0); i < n; i++ {
- nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
+ nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000)
if err != nil {
return err
}
@@ -2991,7 +2991,7 @@ func (t *ImageConfigRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "$type":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -3002,7 +3002,7 @@ func (t *ImageConfigRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "manifest":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -3013,7 +3013,7 @@ func (t *ImageConfigRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "createdAt":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
@@ -3024,7 +3024,7 @@ func (t *ImageConfigRecord) UnmarshalCBOR(r io.Reader) (err error) {
case "configJson":
{
- sval, err := cbg.ReadStringWithMax(cr, 8192)
+ sval, err := cbg.ReadStringWithMax(cr, 1000000)
if err != nil {
return err
}
diff --git a/pkg/atproto/generate.go b/pkg/atproto/generate.go
index 8058af5..bc68509 100644
--- a/pkg/atproto/generate.go
+++ b/pkg/atproto/generate.go
@@ -25,8 +25,15 @@ import (
)
func main() {
- // Generate map-style encoders
- if err := cbg.WriteMapEncodersToFile("cbor_gen.go", "atproto",
+ // MaxStringLength bumps the package-wide string read limit. cbor-gen v0.3.1
+ // honors per-field `cborgen:"...,maxlen=N"` tags only on the write side; the
+ // unmarshal template hard-codes the package default. ImageConfigRecord's
+ // configJson holds raw OCI image configs that routinely exceed the stock
+ // 8KB default (deep build histories, verbose created_by lines), so we lift
+ // the ceiling for everyone. Per-field write caps still apply.
+ gen := cbg.Gen{MaxStringLength: 1_000_000}
+
+ if err := gen.WriteMapEncodersToFile("cbor_gen.go", "atproto",
atproto.CrewRecord{},
atproto.CaptainRecord{},
atproto.LayerRecord{},
diff --git a/pkg/atproto/lexicon.go b/pkg/atproto/lexicon.go
index 360ed82..54e7170 100644
--- a/pkg/atproto/lexicon.go
+++ b/pkg/atproto/lexicon.go
@@ -932,9 +932,9 @@ func ScanRecordKey(manifestDigest string) string {
// Stores the full OCI config JSON so the appview can display layer history including empty layers
type ImageConfigRecord struct {
Type string `json:"$type" cborgen:"$type"`
- Manifest string `json:"manifest" cborgen:"manifest"` // AT-URI of the manifest
- ConfigJSON string `json:"configJson" cborgen:"configJson"` // Raw OCI image config JSON
- CreatedAt string `json:"createdAt" cborgen:"createdAt"` // RFC3339 timestamp
+ Manifest string `json:"manifest" cborgen:"manifest"` // AT-URI of the manifest
+ ConfigJSON string `json:"configJson" cborgen:"configJson,maxlen=1000000"` // Raw OCI image config JSON. Cap mirrors lexicon maxLength so deep image histories (Bazel, multi-stage builds with verbose created_by lines) fit.
+ CreatedAt string `json:"createdAt" cborgen:"createdAt"` // RFC3339 timestamp
}
// NewImageConfigRecord creates a new image config record
diff --git a/pkg/atproto/lexicon_test.go b/pkg/atproto/lexicon_test.go
index b7f855b..5a1f4d4 100644
--- a/pkg/atproto/lexicon_test.go
+++ b/pkg/atproto/lexicon_test.go
@@ -1,6 +1,7 @@
package atproto
import (
+ "bytes"
"encoding/json"
"strings"
"testing"
@@ -1385,3 +1386,72 @@ func TestRepoPageRecord_JSONSerialization(t *testing.T) {
t.Errorf("Avatar.Ref.Link = %v, want %v", decoded.Avatar.Ref.Link, record.Avatar.Ref.Link)
}
}
+
+// TestImageConfigRecord_CBORRoundTrip locks in that we can encode and decode
+// large OCI image configs without hitting cbor-gen's default 8KB string cap.
+// Real-world images with deep build histories (Bazel, multi-stage Dockerfiles)
+// routinely produce config blobs that blow past the default; if either side
+// regresses, the backfill silently drops records with "configJson was too
+// long" instead of populating the layer-history UI.
+func TestImageConfigRecord_CBORRoundTrip(t *testing.T) {
+ tests := []struct {
+ name string
+ payloadSize int
+ }{
+ // Small payloads exercise the happy path — should always have worked.
+ {"small", 1024},
+ // 16KB is well past the old 8192 cborgen default. Pre-fix this would
+ // have failed at marshal time.
+ {"medium-16kb", 16 * 1024},
+ // 200KB matches the upper end of pathological real configs (deep
+ // Bazel histories with verbose created_by lines).
+ {"large-200kb", 200 * 1024},
+ // 900KB is just under our 1MB cap — the read side previously
+ // hard-coded 8192 even with the per-field write tag, so this also
+ // guards against the unmarshal regression.
+ {"near-cap-900kb", 900 * 1024},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ // Use a repeating non-trivial pattern so any byte-level corruption
+ // in encode/decode shows up as a mismatch rather than blending
+ // into a sea of identical bytes.
+ const chunk = "history entry: bazel build //pkg:foo # "
+ payload := strings.Repeat(chunk, tc.payloadSize/len(chunk)+1)[:tc.payloadSize]
+
+ record := &ImageConfigRecord{
+ Type: ImageConfigCollection,
+ Manifest: "at://did:plc:test/io.atcr.manifest/abc",
+ ConfigJSON: payload,
+ CreatedAt: time.Now().UTC().Format(time.RFC3339),
+ }
+
+ var buf bytes.Buffer
+ if err := record.MarshalCBOR(&buf); err != nil {
+ t.Fatalf("MarshalCBOR(%d bytes): %v", tc.payloadSize, err)
+ }
+
+ var decoded ImageConfigRecord
+ if err := decoded.UnmarshalCBOR(&buf); err != nil {
+ t.Fatalf("UnmarshalCBOR(%d bytes): %v", tc.payloadSize, err)
+ }
+
+ if decoded.Type != record.Type {
+ t.Errorf("Type = %q, want %q", decoded.Type, record.Type)
+ }
+ if decoded.Manifest != record.Manifest {
+ t.Errorf("Manifest = %q, want %q", decoded.Manifest, record.Manifest)
+ }
+ if decoded.CreatedAt != record.CreatedAt {
+ t.Errorf("CreatedAt = %q, want %q", decoded.CreatedAt, record.CreatedAt)
+ }
+ if len(decoded.ConfigJSON) != len(record.ConfigJSON) {
+ t.Fatalf("ConfigJSON length = %d, want %d", len(decoded.ConfigJSON), len(record.ConfigJSON))
+ }
+ if decoded.ConfigJSON != record.ConfigJSON {
+ t.Errorf("ConfigJSON content mismatch at length %d", len(record.ConfigJSON))
+ }
+ })
+ }
+}
diff --git a/pkg/hold/admin/admin.go b/pkg/hold/admin/admin.go
index 80e2c23..3f90c45 100644
--- a/pkg/hold/admin/admin.go
+++ b/pkg/hold/admin/admin.go
@@ -90,6 +90,21 @@ type AdminUI struct {
// In-memory session storage (single user, no persistence needed)
sessions map[string]*AdminSession
sessionsMu sync.RWMutex
+
+ // scan-backfill state — runs as a background goroutine on click; the
+ // status endpoint reads this for progress polling. Only one run at a
+ // time (idempotent, so re-running is safe but pointless).
+ scanBackfill scanBackfillState
+}
+
+// scanBackfillState tracks the in-flight scan-status backfill run.
+type scanBackfillState struct {
+ mu sync.Mutex
+ running bool
+ startedAt time.Time
+ current *pds.ScanBackfillResult // running totals (snapshot)
+ result *pds.ScanBackfillResult // final result, set when running=false
+ err string // last error (running ends with err set)
}
// adminContextKey is used to store session data in request context
@@ -532,6 +547,12 @@ func (ui *AdminUI) RegisterRoutes(r chi.Router) {
r.Get("/admin/api/relay/status", ui.handleRelayStatus)
r.Get("/admin/api/crew/member", ui.handleCrewMemberInfo)
+ // Scan-record backfill: kicks off a background run and returns a
+ // progress fragment that polls /status. Use Accept:application/json
+ // for a synchronous JSON response (curl-friendly).
+ r.Post("/admin/api/scan-backfill", ui.handleScanBackfill)
+ r.Get("/admin/api/scan-backfill/status", ui.handleScanBackfillStatus)
+
// Logout
r.Post("/admin/auth/logout", ui.handleLogout)
})
diff --git a/pkg/hold/admin/handlers_scan.go b/pkg/hold/admin/handlers_scan.go
new file mode 100644
index 0000000..c06dcb5
--- /dev/null
+++ b/pkg/hold/admin/handlers_scan.go
@@ -0,0 +1,173 @@
+package admin
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "log/slog"
+ "net/http"
+ "strings"
+ "time"
+
+ "atcr.io/pkg/hold/pds"
+)
+
+// handleScanBackfill kicks off a scan-status backfill in a background
+// goroutine and returns a progress fragment that polls
+// /admin/api/scan-backfill/status for updates. Idempotent — clicking again
+// while a run is in flight just shows the current progress.
+//
+// Why background: reverse proxies typically cap upstream HTTP timeouts at
+// 10–60s, which would cancel a synchronous request mid-loop. Detaching the
+// work from the request context lets it run to completion.
+//
+// JSON callers (Accept: application/json) get a synchronous run instead —
+// useful for curl + scripting.
+func (ui *AdminUI) handleScanBackfill(w http.ResponseWriter, r *http.Request) {
+ session := getSessionFromContext(r.Context())
+ wantJSON := strings.Contains(r.Header.Get("Accept"), "application/json")
+
+ if wantJSON {
+ // Synchronous JSON path — caller decides their own timeout.
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
+ defer cancel()
+ res, err := ui.pds.BackfillScanStatus(ctx, scanBackfillLogger, nil)
+ if err != nil {
+ slog.Error("scan-backfill failed", "by", session.DID, "error", err)
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ slog.Info("scan-status backfill complete (sync)",
+ "by", session.DID,
+ "scanned", res.Scanned,
+ "already_tagged", res.AlreadyTagged,
+ "marked_skipped", res.MarkedSkipped,
+ "marked_failed", res.MarkedFailed,
+ "rewritten", res.Rewritten,
+ )
+ w.Header().Set("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(res)
+ return
+ }
+
+ // HTML path — kick off the background run if one isn't already going.
+ st := &ui.scanBackfill
+ st.mu.Lock()
+ alreadyRunning := st.running
+ if !alreadyRunning {
+ st.running = true
+ st.startedAt = time.Now()
+ st.current = &pds.ScanBackfillResult{}
+ st.result = nil
+ st.err = ""
+ }
+ st.mu.Unlock()
+
+ if !alreadyRunning {
+ slog.Info("scan-status backfill started via admin panel", "by", session.DID)
+ go ui.runScanBackfill()
+ } else {
+ slog.Debug("scan-status backfill already in progress; returning current state")
+ }
+
+ ui.renderTemplate(w, "partials/scan_backfill_progress.html", ui.snapshotScanBackfill())
+}
+
+// handleScanBackfillStatus is polled by the progress fragment. Returns the
+// progress fragment again if running, the result fragment when done, or an
+// error fragment if something went wrong.
+func (ui *AdminUI) handleScanBackfillStatus(w http.ResponseWriter, r *http.Request) {
+ snap := ui.snapshotScanBackfill()
+ if snap.Running {
+ ui.renderTemplate(w, "partials/scan_backfill_progress.html", snap)
+ return
+ }
+ if snap.Error != "" {
+ ui.renderTemplate(w, "partials/gc_error.html", struct{ Error string }{snap.Error})
+ return
+ }
+ if snap.Result == nil {
+ // Initial state, before any run — render an empty placeholder.
+ _, _ = w.Write([]byte(""))
+ return
+ }
+ ui.renderTemplate(w, "partials/scan_backfill_result.html", snap.Result)
+}
+
+// runScanBackfill is the goroutine body. Updates the shared state as the
+// backfill progresses and stores the final result (or error) when it ends.
+func (ui *AdminUI) runScanBackfill() {
+ st := &ui.scanBackfill
+ defer func() {
+ st.mu.Lock()
+ st.running = false
+ st.mu.Unlock()
+ }()
+
+ // Generous independent timeout — the loop is single-threaded and large
+ // holds with thousands of legacy records can take a few minutes.
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
+ defer cancel()
+
+ res, err := ui.pds.BackfillScanStatus(ctx, scanBackfillLogger, func(snap *pds.ScanBackfillResult) {
+ // Copy so we don't keep a pointer the loop will mutate.
+ c := *snap
+ st.mu.Lock()
+ st.current = &c
+ st.mu.Unlock()
+ })
+ st.mu.Lock()
+ if err != nil {
+ st.err = err.Error()
+ slog.Error("scan-status backfill failed", "error", err)
+ } else {
+ st.result = res
+ slog.Info("scan-status backfill complete",
+ "scanned", res.Scanned,
+ "already_tagged", res.AlreadyTagged,
+ "marked_skipped", res.MarkedSkipped,
+ "marked_failed", res.MarkedFailed,
+ "rewritten", res.Rewritten,
+ )
+ }
+ st.mu.Unlock()
+}
+
+// scanBackfillSnapshot is the shape exposed to templates.
+type scanBackfillSnapshot struct {
+ Running bool
+ StartedAt time.Time
+ Current *pds.ScanBackfillResult // populated while running
+ Result *pds.ScanBackfillResult // populated when complete
+ Error string
+}
+
+// snapshotScanBackfill returns a copy of the current state — safe to render
+// without holding the mutex.
+func (ui *AdminUI) snapshotScanBackfill() scanBackfillSnapshot {
+ st := &ui.scanBackfill
+ st.mu.Lock()
+ defer st.mu.Unlock()
+
+ snap := scanBackfillSnapshot{
+ Running: st.running,
+ StartedAt: st.startedAt,
+ Error: st.err,
+ }
+ if st.current != nil {
+ c := *st.current
+ snap.Current = &c
+ }
+ if st.result != nil {
+ r := *st.result
+ snap.Result = &r
+ }
+ return snap
+}
+
+// scanBackfillLogger formats the printf-style messages from BackfillScanStatus
+// into a single slog message — slog's variadic args are key/value pairs, not
+// printf operands.
+func scanBackfillLogger(format string, args ...any) {
+ slog.Warn("scan-backfill: " + fmt.Sprintf(format, args...))
+}
diff --git a/pkg/hold/admin/templates/partials/scan_backfill_progress.html b/pkg/hold/admin/templates/partials/scan_backfill_progress.html
new file mode 100644
index 0000000..edcf72d
--- /dev/null
+++ b/pkg/hold/admin/templates/partials/scan_backfill_progress.html
@@ -0,0 +1,20 @@
+{{define "partials/scan_backfill_progress.html"}}
+
+
+
+
Backfilling scan records...
+ {{ if .Current }}
+
+ Scanned {{ .Current.Scanned }} records ·
+ rewrites: {{ .Current.Rewritten }}
+ ({{ .Current.MarkedSkipped }} skipped, {{ .Current.MarkedFailed }} failed)
+
+ {{ else }}
+
Starting...
+ {{ end }}
+
+
+{{end}}
diff --git a/pkg/hold/admin/templates/partials/scan_backfill_result.html b/pkg/hold/admin/templates/partials/scan_backfill_result.html
new file mode 100644
index 0000000..73dcd65
--- /dev/null
+++ b/pkg/hold/admin/templates/partials/scan_backfill_result.html
@@ -0,0 +1,15 @@
+{{define "partials/scan_backfill_result.html"}}
+
+ {{ icon "check-circle" "size-4 shrink-0" }}
+
+
Scan-record backfill complete
+
+ - Scanned: {{ .Scanned }}
+ - Already tagged: {{ .AlreadyTagged }}
+ - → Skipped (helm / in-toto / DSSE): {{ .MarkedSkipped }}
+ - → Failed (legacy errors): {{ .MarkedFailed }}
+ - Total rewritten: {{ .Rewritten }}
+
+
+
+{{end}}
diff --git a/pkg/hold/admin/templates/partials/tab_storage.html b/pkg/hold/admin/templates/partials/tab_storage.html
index 7ace056..07b132f 100644
--- a/pkg/hold/admin/templates/partials/tab_storage.html
+++ b/pkg/hold/admin/templates/partials/tab_storage.html
@@ -67,4 +67,31 @@
{{end}}
+
+
+