Documentation
¶
Index ¶
- Constants
- Variables
- func DeriveKEK(signingSecret string) [32]byte
- func ValidBucketName(name string) bool
- type Account
- type Bucket
- type DB
- func (db *DB) AbortMultipartUpload(uploadID string) (*MultipartUpload, error)
- func (db *DB) AddMultipartPart(uploadID string, part MultipartPart) error
- func (db *DB) Backup(w io.Writer) error
- func (db *DB) BucketStats(bucketID string) (count int64, totalSize int64, err error)
- func (db *DB) CleanupExpiredUploads(maxAge time.Duration) ([]MultipartUpload, error)
- func (db *DB) Close() error
- func (db *DB) CompleteMultipartUpload(uploadID string, partNumbers []int) (*MultipartUpload, error)
- func (db *DB) CreateAccount(a *Account) error
- func (db *DB) CreateAccountIfNotExists(name string) (*Account, bool, error)
- func (db *DB) CreateBucket(b *Bucket) error
- func (db *DB) CreateMultipartUpload(upload *MultipartUpload) error
- func (db *DB) CreateToken(t *Token) error
- func (db *DB) CreateTokenIfNotExists(tokenID, accountID, name, secretHash, plaintextSecret string, ...) (*Token, TokenSeedStatus, error)
- func (db *DB) DeleteBucket(name string) error
- func (db *DB) DeleteMultipartUpload(uploadID string) error
- func (db *DB) DeleteObjectMeta(bucketID, key string) (*Object, error)
- func (db *DB) DeleteObjectMetaAny(bucketID, key string) error
- func (db *DB) ForEachObject(bucketID string, fn func(Object) error) error
- func (db *DB) ForEachObjectFrom(bucketID, startKey string, limit int, fn func(Object) error) (lastKey string, err error)
- func (db *DB) GetAccount(id string) (*Account, error)
- func (db *DB) GetBucket(name string) (*Bucket, error)
- func (db *DB) GetBucketAndObject(bucketName, key string) (*Bucket, *Object, error)
- func (db *DB) GetBucketByID(id string) (*Bucket, error)
- func (db *DB) GetMultipartUpload(uploadID string) (*MultipartUpload, error)
- func (db *DB) GetObjectMeta(bucketID, key string) (*Object, error)
- func (db *DB) GetObjectMetaAny(bucketID, key string) (*Object, error)
- func (db *DB) GetToken(tokenID string) (*Token, error)
- func (db *DB) ListBuckets(ownerAccountID string) ([]Bucket, error)
- func (db *DB) ListMultipartUploads(bucketID string) ([]MultipartUpload, error)
- func (db *DB) ListObjects(bucketID, prefix, delimiter, startAfter string, maxKeys int) (*ListObjectsResult, error)
- func (db *DB) ListTokens(accountID string) ([]Token, error)
- func (db *DB) MigrateLegacyObject(bucketID, key string) (bool, error)
- func (db *DB) MigrateTokenSecrets() (migrated int, err error)
- func (db *DB) Path() string
- func (db *DB) PutObjectMeta(obj *Object) (*Object, error)
- func (db *DB) QuarantineObject(bucketID, key string) error
- func (db *DB) RebuildAllBucketStatsIfMissing() error
- func (db *DB) RebuildBucketStats(bucketID string) error
- func (db *DB) RekeyTokens(oldSecret, newSecret string) (int, error)
- func (db *DB) RestoreObject(bucketID, key string) error
- func (db *DB) RevokeToken(tokenID string) error
- func (db *DB) SetDeletionHook(fn func())
- func (db *DB) SetSigningSecret(s string)
- func (db *DB) SetTokenInvalidateHook(fn func(tokenID string))
- func (db *DB) UpdateBucketPolicy(name string, policy json.RawMessage) error
- type ListObjectsResult
- type MultipartPart
- type MultipartUpload
- type Object
- type Token
- type TokenSeedStatus
Constants ¶
const ( ActionBucketList = "bucket:list" ActionBucketReadMeta = "bucket:read-meta" ActionBucketWriteMeta = "bucket:write-meta" ActionObjectGet = "object:get" ActionObjectPut = "object:put" ActionObjectDelete = "object:delete" ActionObjectList = "object:list" ActionMultipartCreate = "multipart:create" ActionMultipartUpload = "multipart:upload-part" ActionMultipartComplete = "multipart:complete" ActionMultipartAbort = "multipart:abort" )
Actions
const MaxBucketsPerAccount = 1000
MaxBucketsPerAccount is the maximum number of buckets a single account may own.
const MaxMultipartParts = 10000
Variables ¶
var ( ErrBucketExists = errors.New("bucket already exists") ErrBucketNotFound = errors.New("bucket not found") ErrBucketNotEmpty = errors.New("bucket is not empty") ErrBucketLimitExceeded = errors.New("account bucket limit exceeded") )
var ( ErrUploadNotFound = errors.New("upload not found") ErrUploadNotActive = errors.New("upload is not active") ErrInvalidPartNumber = errors.New("part number must be between 1 and 10000") ErrTooManyParts = errors.New("upload exceeds maximum number of parts (10000)") )
var ( ErrTokenNotFound = errors.New("token not found") ErrAccountNotFound = errors.New("account not found") )
var AllActions = []string{ ActionBucketList, ActionBucketReadMeta, ActionBucketWriteMeta, ActionObjectGet, ActionObjectPut, ActionObjectDelete, ActionObjectList, ActionMultipartCreate, ActionMultipartUpload, ActionMultipartComplete, ActionMultipartAbort, }
AllActions is the full set of actions for admin/full-access tokens.
var (
ErrObjectNotFound = errors.New("object not found")
)
Functions ¶
func ValidBucketName ¶
ValidBucketName returns true if name is a legal S3-style bucket name.
Types ¶
type Bucket ¶
type Bucket struct {
ID string `json:"id"`
Name string `json:"name"`
OwnerAccountID string `json:"owner_account_id"`
CreatedAt time.Time `json:"created_at"`
Visibility string `json:"visibility"` // "private", "public-read"
PolicyJSON json.RawMessage `json:"policy_json,omitempty"`
Status string `json:"status"` // "active", "deleting"
}
type DB ¶
type DB struct {
// contains filtered or unexported fields
}
DB wraps a bbolt database for Jay metadata.
func Open ¶
Open opens or creates the bbolt database at the given path. It creates the parent directory if needed and bootstraps top-level buckets.
func (*DB) AbortMultipartUpload ¶
func (db *DB) AbortMultipartUpload(uploadID string) (*MultipartUpload, error)
AbortMultipartUpload marks the upload as aborted.
func (*DB) AddMultipartPart ¶
func (db *DB) AddMultipartPart(uploadID string, part MultipartPart) error
AddMultipartPart adds or replaces a part in a multipart upload.
func (*DB) Backup ¶
Backup writes a consistent hot copy of the bbolt database to w using the existing handle. It runs inside a read transaction, so callers do not need to (and must not) open a second bolt handle on the same file.
func (*DB) BucketStats ¶
BucketStats returns the count and total size of active objects in a bucket by reading the maintained counter in O(1). A missing entry returns (0, 0, nil).
func (*DB) CleanupExpiredUploads ¶
func (db *DB) CleanupExpiredUploads(maxAge time.Duration) ([]MultipartUpload, error)
CleanupExpiredUploads removes uploads older than maxAge.
func (*DB) CompleteMultipartUpload ¶
func (db *DB) CompleteMultipartUpload(uploadID string, partNumbers []int) (*MultipartUpload, error)
CompleteMultipartUpload marks the upload as completed and returns the sorted parts.
func (*DB) CreateAccount ¶
CreateAccount creates a new account.
func (*DB) CreateAccountIfNotExists ¶
CreateAccountIfNotExists looks up an account by Name. If found, returns it with created=false. If not found, creates a new one with a uuid ID.
func (*DB) CreateBucket ¶
CreateBucket creates a new bucket. Returns ErrBucketExists if the name is taken.
func (*DB) CreateMultipartUpload ¶
func (db *DB) CreateMultipartUpload(upload *MultipartUpload) error
CreateMultipartUpload starts a new multipart upload.
func (*DB) CreateToken ¶
CreateToken stores a new token in bbolt. The caller passes a Token with a plaintext SecretKey; it is encrypted before being persisted.
func (*DB) CreateTokenIfNotExists ¶
func (db *DB) CreateTokenIfNotExists(tokenID, accountID, name, secretHash, plaintextSecret string, allowedActions []string) (*Token, TokenSeedStatus, error)
CreateTokenIfNotExists creates a token with a caller-provided ID. If a token with the same ID already exists, it compares the plaintext secret against the stored bcrypt hash:
- match -> returns TokenSeedReused (no write)
- mismatch -> returns TokenSeedMismatch (no write)
Otherwise creates a new token with the supplied hash and returns TokenSeedCreated.
func (*DB) DeleteBucket ¶
DeleteBucket removes a bucket by name. Fails if objects exist in it.
func (*DB) DeleteMultipartUpload ¶
DeleteMultipartUpload removes a completed/aborted upload record.
func (*DB) DeleteObjectMeta ¶
DeleteObjectMeta marks an object as deleted and returns it for physical GC.
func (*DB) DeleteObjectMetaAny ¶
DeleteObjectMetaAny removes object metadata regardless of state.
func (*DB) ForEachObject ¶
ForEachObject iterates all objects in a bucket (all states) and calls fn for each.
func (*DB) ForEachObjectFrom ¶
func (db *DB) ForEachObjectFrom(bucketID, startKey string, limit int, fn func(Object) error) (lastKey string, err error)
ForEachObjectFrom iterates objects starting from startKey (exclusive), up to limit. Returns the key of the last object visited (for resumption) and any error.
func (*DB) GetAccount ¶
GetAccount retrieves an account by ID.
func (*DB) GetBucketAndObject ¶
GetBucketAndObject resolves a bucket by name and fetches an active object by key in a single bbolt View transaction. Returns ErrBucketNotFound if the bucket is missing and ErrObjectNotFound if the object is missing or not in the "active" state. When the bucket exists but the object does not, the bucket pointer is still returned alongside ErrObjectNotFound so callers can authorize without issuing a second view transaction.
func (*DB) GetBucketByID ¶
GetBucketByID retrieves a bucket by its ID.
func (*DB) GetMultipartUpload ¶
func (db *DB) GetMultipartUpload(uploadID string) (*MultipartUpload, error)
GetMultipartUpload retrieves a multipart upload by ID.
func (*DB) GetObjectMeta ¶
GetObjectMeta retrieves object metadata by bucket ID and key.
func (*DB) GetObjectMetaAny ¶
GetObjectMetaAny retrieves object metadata regardless of state.
func (*DB) ListBuckets ¶
ListBuckets returns all buckets, optionally filtered by owner account.
func (*DB) ListMultipartUploads ¶
func (db *DB) ListMultipartUploads(bucketID string) ([]MultipartUpload, error)
ListMultipartUploads returns active uploads for a bucket.
func (*DB) ListObjects ¶
func (db *DB) ListObjects(bucketID, prefix, delimiter, startAfter string, maxKeys int) (*ListObjectsResult, error)
ListObjects lists objects in a bucket with prefix, delimiter, pagination support.
Iteration is split into short read transactions (batchSize keys each) so that long listings do not starve bbolt writers (bbolt allows a single writer and blocks it for the entire lifetime of any overlapping read tx). Between batches the read tx is released, giving writers a chance to commit; the next batch resumes from the last key seen.
Externally observable semantics (returned object set, CommonPrefixes, IsTruncated, NextStartAfter, delimiter handling, prefix matching, maxKeys cap) are preserved bit-identical to the previous single-tx implementation.
func (*DB) ListTokens ¶
ListTokens returns all tokens, optionally filtered by account. SecretHash and SecretKey are zeroed in the response.
func (*DB) MigrateLegacyObject ¶
MigrateLegacyObject rewrites a single object record from the legacy JSON format to the current binary envelope. Returns true if the record was actually rewritten, false if it was already in binary format, missing, or the bucket no longer exists.
Designed to be called from the scrubber after a healthy checksum verify so we only persist re-encoded values for records we have just validated. A single write transaction per object keeps bbolt writer contention low; at the scrub throttle rates (default 10% / 6h) total migration finishes in a handful of cycles without a dedicated batch job.
Does not touch bucket statistics — the record's logical content is unchanged, only its on-disk envelope.
func (*DB) MigrateTokenSecrets ¶
MigrateTokenSecrets scans the tokens bucket and re-encrypts any token where SecretKey is stored as plaintext (no "enc:v1:" prefix). Returns the number of tokens migrated. Idempotent — already-encrypted entries are skipped.
func (*DB) PutObjectMeta ¶
PutObjectMeta creates or updates object metadata within a bbolt write transaction. Returns the previous object (if overwriting) for GC of the old physical file.
func (*DB) QuarantineObject ¶
QuarantineObject marks an object as quarantined in metadata.
func (*DB) RebuildAllBucketStatsIfMissing ¶
RebuildAllBucketStatsIfMissing iterates every bucket and rebuilds the maintained stats entry only for those whose counter is absent. Existing entries (including zero-valued ones) are left untouched.
func (*DB) RebuildBucketStats ¶
RebuildBucketStats walks obj:<bucketID> and recomputes the maintained counter from active object records.
func (*DB) RekeyTokens ¶
RekeyTokens decrypts every token secret with oldSecret and re-encrypts it with newSecret. Jay must be stopped before calling this — bbolt enforces an exclusive file lock that will block if another process has the DB open. Returns the number of tokens rekeyed. Idempotent if run again with same args.
func (*DB) RestoreObject ¶
RestoreObject sets a quarantined object's state back to "active".
func (*DB) RevokeToken ¶
RevokeToken marks a token as revoked.
func (*DB) SetDeletionHook ¶
func (db *DB) SetDeletionHook(fn func())
SetDeletionHook registers a callback invoked after a successful DeleteObjectMeta commit. Safe to call with nil to clear. Single callback per DB instance — a second call overwrites.
func (*DB) SetSigningSecret ¶
SetSigningSecret derives and caches the KEK used to encrypt/decrypt Token.SecretKey at rest. Must be called before any token read/write.
func (*DB) SetTokenInvalidateHook ¶
SetTokenInvalidateHook registers a callback invoked after a token is persisted with a mutated state (revoke/update/delete). The auth layer wires its cache invalidator here so stale cache entries are purged immediately instead of waiting for TTL expiration.
Passing nil clears the hook. Safe to call at any time; invocations are serialized via hookMu.
func (*DB) UpdateBucketPolicy ¶
func (db *DB) UpdateBucketPolicy(name string, policy json.RawMessage) error
UpdateBucketPolicy updates the policy JSON for a bucket.
type ListObjectsResult ¶
type ListObjectsResult struct {
Objects []Object
CommonPrefixes []string
IsTruncated bool
NextStartAfter string
}
ListObjectsResult holds the result of a ListObjects call.
type MultipartPart ¶
type MultipartPart struct {
PartNumber int `json:"part_number"`
Size int64 `json:"size"`
ETag string `json:"etag"`
ChecksumSHA256 string `json:"checksum_sha256"`
LocationRef string `json:"location_ref"`
CreatedAt time.Time `json:"created_at"`
}
MultipartPart represents a single part in a multipart upload.
type MultipartUpload ¶
type MultipartUpload struct {
UploadID string `json:"upload_id"`
BucketID string `json:"bucket_id"`
ObjectKey string `json:"object_key"`
ContentType string `json:"content_type,omitempty"`
InitiatedBy string `json:"initiated_by"`
CreatedAt time.Time `json:"created_at"`
State string `json:"state"` // "initiated", "completed", "aborted"
Parts []MultipartPart `json:"parts,omitempty"`
}
MultipartUpload tracks an in-progress multipart upload.
type Object ¶
type Object struct {
BucketID string `json:"bucket_id"`
Key string `json:"key"`
ObjectID string `json:"object_id"`
State string `json:"state"` // "active", "deleted", "quarantined"
SizeBytes int64 `json:"size_bytes"`
ContentType string `json:"content_type"`
ETag string `json:"etag"`
ChecksumSHA256 string `json:"checksum_sha256"`
LocationRef string `json:"location_ref"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
MetadataHeaders map[string]string `json:"metadata_headers,omitempty"`
}
type Token ¶
type Token struct {
TokenID string `json:"token_id"`
AccountID string `json:"account_id"`
Name string `json:"name"`
SecretHash string `json:"secret_hash"`
SecretKey string `json:"secret_key,omitempty"` // plaintext secret for SigV4 HMAC computation
AllowedActions []string `json:"allowed_actions"`
BucketScope []string `json:"bucket_scope,omitempty"`
PrefixScope []string `json:"prefix_scope,omitempty"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
Status string `json:"status"` // "active", "revoked"
}
type TokenSeedStatus ¶
type TokenSeedStatus int
TokenSeedStatus describes the outcome of CreateTokenIfNotExists.
const ( TokenSeedCreated TokenSeedStatus = iota // brand new token persisted TokenSeedReused // token existed with matching secret hash TokenSeedMismatch // token existed but secret hash didn't match )