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
+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