cbor_gen crew

This commit is contained in:
Evan Jarrett
2025-10-14 22:11:00 -05:00
parent 12935490d4
commit 21e6d08f75
10 changed files with 370 additions and 47 deletions
+32
View File
@@ -0,0 +1,32 @@
package main
// CBOR Code Generator
//
// This generates optimized CBOR marshaling code for ATProto records.
//
// Usage:
// go run gen/main.go
//
// This creates pkg/hold/pds/cbor_gen.go which should be committed to git.
// Only re-run when you modify types in pkg/hold/pds/types.go
import (
"fmt"
"os"
cbg "github.com/whyrusleeping/cbor-gen"
"atcr.io/pkg/hold/pds"
)
func main() {
// Generate map-style encoders for CrewRecord
if err := cbg.WriteMapEncodersToFile("pkg/hold/pds/cbor_gen.go", "pds",
pds.CrewRecord{},
); err != nil {
fmt.Printf("Failed to generate CBOR encoders: %v\n", err)
os.Exit(1)
}
fmt.Println("Generated CBOR encoders in pkg/hold/pds/cbor_gen.go")
}
+3 -3
View File
@@ -11,13 +11,15 @@ require (
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.3
github.com/ipfs/go-cid v0.4.1
github.com/klauspost/compress v1.18.0
github.com/mattn/go-sqlite3 v1.14.32
github.com/opencontainers/go-digest v1.0.0
github.com/spf13/cobra v1.8.0
github.com/whyrusleeping/cbor-gen v0.3.1
go.yaml.in/yaml/v4 v4.0.0-rc.2
golang.org/x/crypto v0.39.0
github.com/ipfs/go-cid v0.4.1
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028
)
require (
@@ -100,7 +102,6 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e // indirect
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect
go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 // indirect
@@ -133,7 +134,6 @@ require (
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.6.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
google.golang.org/grpc v1.68.0 // indirect
+2 -2
View File
@@ -370,8 +370,8 @@ github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSD
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 h1:5HZfQkwe0mIfyDmc1Em5GqlNRzcdtlv4HTNmdpt7XH0=
github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11/go.mod h1:Wlo/SzPmxVp6vXpGt/zaXhHH0fn4IxgqZc82aKg6bpQ=
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4=
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so=
github.com/whyrusleeping/cbor-gen v0.3.1 h1:82ioxmhEYut7LBVGhGq8xoRkXPLElVuh5mV67AFfdv0=
github.com/whyrusleeping/cbor-gen v0.3.1/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+1 -1
View File
@@ -163,7 +163,7 @@ func (s *HoldService) isCrewMember(did string) (bool, error) {
log.Printf("Warning: failed to resolve handle for DID %s: %v", did, err)
// Continue checking explicit DIDs even if handle resolution fails
handleResolved = true // Mark as attempted (don't retry)
handle = "" // Empty handle won't match patterns
handle = "" // Empty handle won't match patterns
} else {
handleResolved = true
}
+295
View File
@@ -0,0 +1,295 @@
// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT.
package pds
import (
"fmt"
"io"
"math"
"sort"
cid "github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen"
xerrors "golang.org/x/xerrors"
)
var _ = xerrors.Errorf
var _ = cid.Undef
var _ = math.E
var _ = sort.Sort
func (t *CrewRecord) MarshalCBOR(w io.Writer) error {
if t == nil {
_, err := w.Write(cbg.CborNull)
return err
}
cw := cbg.NewCborWriter(w)
if _, err := cw.Write([]byte{165}); err != nil {
return err
}
// t.Role (string) (string)
if len("role") > 8192 {
return xerrors.Errorf("Value in field \"role\" was too long")
}
if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("role"))); err != nil {
return err
}
if _, err := cw.WriteString(string("role")); err != nil {
return err
}
if len(t.Role) > 8192 {
return xerrors.Errorf("Value in field t.Role was too long")
}
if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Role))); err != nil {
return err
}
if _, err := cw.WriteString(string(t.Role)); err != nil {
return err
}
// t.Type (string) (string)
if len("$type") > 8192 {
return xerrors.Errorf("Value in field \"$type\" was too long")
}
if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil {
return err
}
if _, err := cw.WriteString(string("$type")); err != nil {
return err
}
if len(t.Type) > 8192 {
return xerrors.Errorf("Value in field t.Type was too long")
}
if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Type))); err != nil {
return err
}
if _, err := cw.WriteString(string(t.Type)); err != nil {
return err
}
// t.Member (string) (string)
if len("member") > 8192 {
return xerrors.Errorf("Value in field \"member\" was too long")
}
if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("member"))); err != nil {
return err
}
if _, err := cw.WriteString(string("member")); err != nil {
return err
}
if len(t.Member) > 8192 {
return xerrors.Errorf("Value in field t.Member was too long")
}
if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Member))); err != nil {
return err
}
if _, err := cw.WriteString(string(t.Member)); err != nil {
return err
}
// t.AddedAt (string) (string)
if len("addedAt") > 8192 {
return xerrors.Errorf("Value in field \"addedAt\" was too long")
}
if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("addedAt"))); err != nil {
return err
}
if _, err := cw.WriteString(string("addedAt")); err != nil {
return err
}
if len(t.AddedAt) > 8192 {
return xerrors.Errorf("Value in field t.AddedAt was too long")
}
if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.AddedAt))); err != nil {
return err
}
if _, err := cw.WriteString(string(t.AddedAt)); err != nil {
return err
}
// t.Permissions ([]string) (slice)
if len("permissions") > 8192 {
return xerrors.Errorf("Value in field \"permissions\" was too long")
}
if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("permissions"))); err != nil {
return err
}
if _, err := cw.WriteString(string("permissions")); err != nil {
return err
}
if len(t.Permissions) > 8192 {
return xerrors.Errorf("Slice value in field t.Permissions was too long")
}
if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Permissions))); err != nil {
return err
}
for _, v := range t.Permissions {
if len(v) > 8192 {
return xerrors.Errorf("Value in field v was too long")
}
if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil {
return err
}
if _, err := cw.WriteString(string(v)); err != nil {
return err
}
}
return nil
}
func (t *CrewRecord) UnmarshalCBOR(r io.Reader) (err error) {
*t = CrewRecord{}
cr := cbg.NewCborReader(r)
maj, extra, err := cr.ReadHeader()
if err != nil {
return err
}
defer func() {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
}()
if maj != cbg.MajMap {
return fmt.Errorf("cbor input should be of type map")
}
if extra > cbg.MaxLength {
return fmt.Errorf("CrewRecord: map struct too large (%d)", extra)
}
n := extra
nameBuf := make([]byte, 11)
for i := uint64(0); i < n; i++ {
nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192)
if err != nil {
return err
}
if !ok {
// Field doesn't exist on this type, so ignore it
if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil {
return err
}
continue
}
switch string(nameBuf[:nameLen]) {
// t.Role (string) (string)
case "role":
{
sval, err := cbg.ReadStringWithMax(cr, 8192)
if err != nil {
return err
}
t.Role = string(sval)
}
// t.Type (string) (string)
case "$type":
{
sval, err := cbg.ReadStringWithMax(cr, 8192)
if err != nil {
return err
}
t.Type = string(sval)
}
// t.Member (string) (string)
case "member":
{
sval, err := cbg.ReadStringWithMax(cr, 8192)
if err != nil {
return err
}
t.Member = string(sval)
}
// t.AddedAt (string) (string)
case "addedAt":
{
sval, err := cbg.ReadStringWithMax(cr, 8192)
if err != nil {
return err
}
t.AddedAt = string(sval)
}
// t.Permissions ([]string) (slice)
case "permissions":
maj, extra, err = cr.ReadHeader()
if err != nil {
return err
}
if extra > 8192 {
return fmt.Errorf("t.Permissions: array too large (%d)", extra)
}
if maj != cbg.MajArray {
return fmt.Errorf("expected cbor array")
}
if extra > 0 {
t.Permissions = make([]string, extra)
}
for i := 0; i < int(extra); i++ {
{
var maj byte
var extra uint64
var err error
_ = maj
_ = extra
_ = err
{
sval, err := cbg.ReadStringWithMax(cr, 8192)
if err != nil {
return err
}
t.Permissions[i] = string(sval)
}
}
}
default:
// Field doesn't exist on this type, so ignore it
if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil {
return err
}
}
}
return nil
}
+2 -26
View File
@@ -3,43 +3,19 @@ package pds
import (
"context"
"fmt"
"io"
"time"
"github.com/ipfs/go-cid"
)
// CrewRecord represents a crew member in the hold
type CrewRecord struct {
Member string `json:"member" cborgen:"member"` // DID of the crew member
Role string `json:"role" cborgen:"role"` // "admin" or "member"
Permissions []string `json:"permissions" cborgen:"permissions"` // e.g., ["blob:read", "blob:write"]
AddedAt time.Time `json:"addedAt" cborgen:"addedAt"`
}
// MarshalCBOR implements cbg.CBORMarshaler
func (c *CrewRecord) MarshalCBOR(w io.Writer) error {
// TODO: Implement proper CBOR marshaling
return fmt.Errorf("CBOR marshaling not yet implemented")
}
// UnmarshalCBOR implements cbg.CBORUnmarshaler
func (c *CrewRecord) UnmarshalCBOR(r io.Reader) error {
// TODO: Implement proper CBOR unmarshaling
return fmt.Errorf("CBOR unmarshaling not yet implemented")
}
const (
CrewCollection = "io.atcr.hold.crew"
)
// AddCrewMember adds a new crew member to the hold and commits to carstore
func (p *HoldPDS) AddCrewMember(ctx context.Context, memberDID, role string, permissions []string) (cid.Cid, error) {
crewRecord := &CrewRecord{
Type: CrewCollection,
Member: memberDID,
Role: role,
Permissions: permissions,
AddedAt: time.Now(),
AddedAt: time.Now().Format(time.RFC3339),
}
// Create record in repo (using memberDID as rkey for easy lookup)
+7 -8
View File
@@ -9,13 +9,13 @@ import (
// DIDDocument represents a did:web document
type DIDDocument struct {
Context []string `json:"@context"`
ID string `json:"id"`
AlsoKnownAs []string `json:"alsoKnownAs,omitempty"`
VerificationMethod []VerificationMethod `json:"verificationMethod"`
Authentication []string `json:"authentication,omitempty"`
AssertionMethod []string `json:"assertionMethod,omitempty"`
Service []Service `json:"service,omitempty"`
Context []string `json:"@context"`
ID string `json:"id"`
AlsoKnownAs []string `json:"alsoKnownAs,omitempty"`
VerificationMethod []VerificationMethod `json:"verificationMethod"`
Authentication []string `json:"authentication,omitempty"`
AssertionMethod []string `json:"assertionMethod,omitempty"`
Service []Service `json:"service,omitempty"`
}
// VerificationMethod represents a public key in a DID document
@@ -83,7 +83,6 @@ func (p *HoldPDS) GenerateDIDDocument(publicURL string) (*DIDDocument, error) {
return doc, nil
}
// MarshalDIDDocument converts a DID document to JSON using the stored public URL
func (p *HoldPDS) MarshalDIDDocument() ([]byte, error) {
doc, err := p.GenerateDIDDocument(p.publicURL)
+9 -4
View File
@@ -101,15 +101,20 @@ func (p *HoldPDS) SigningKey() *crypto.PrivateKeyK256 {
// Bootstrap initializes the hold with the owner as the first crew member
func (p *HoldPDS) Bootstrap(ctx context.Context, ownerDID string) error {
if ownerDID == "" {
// No owner specified, skip bootstrap
return nil
}
// Check if repo already has commits
_, err := p.carstore.GetUserRepoHead(ctx, p.uid)
head, err := p.carstore.GetUserRepoHead(ctx, p.uid)
if err == nil {
// Repo already has commits, skip bootstrap
return nil
// Repo exists - check if we need to re-bootstrap due to key change
// If the repo exists but is empty/invalid, we should re-bootstrap
if head.String() == "" || head.String() == "b" {
fmt.Printf("⚠️ Detected invalid repo state, re-bootstrapping...\n")
} else {
fmt.Printf("⏭️ Skipping PDS bootstrap: repo already initialized (head: %s)\n", head.String()[:16])
return nil
}
}
// Add hold owner as first crew member with admin role
+16
View File
@@ -0,0 +1,16 @@
package pds
// ATProto record types for the hold service
// CrewRecord represents a crew member in the hold
type CrewRecord struct {
Type string `cborgen:"$type"`
Member string `cborgen:"member"`
Role string `cborgen:"role"`
Permissions []string `cborgen:"permissions"`
AddedAt string `cborgen:"addedAt"` // RFC3339 timestamp
}
const (
CrewCollection = "io.atcr.hold.crew"
)
+3 -3
View File
@@ -11,9 +11,9 @@ import (
// XRPCHandler handles XRPC requests for the embedded PDS
type XRPCHandler struct {
pds *HoldPDS
publicURL string
blobStore BlobStore
pds *HoldPDS
publicURL string
blobStore BlobStore
}
// BlobStore interface wraps the existing hold service storage operations