Documentation
¶
Overview ¶
Package contract wires Trove into the Forge dashboard's contract path. It registers the `trove` contributor and answers its intents from the live Trove stores and their drivers, never from the extension's metadata store, which normal operation does not write.
Index ¶
Constants ¶
const ( OpDownload = "download" OpPreview = "preview" OpUpload = "upload" )
Ticket operations. A ticket authorises exactly one of these against one object, so a download ticket cannot be replayed as an upload.
const ( DownloadTicketTTL = 60 * time.Second UploadTicketTTL = 15 * time.Minute )
Ticket lifetimes. The browser follows a download or preview link at once. An upload of the largest allowed file on a slow link takes a while.
const ContributorName = "trove"
ContributorName is the join key with packages/plugin-trove's `extension` field and matches the extension's name. A mismatch hides the React plugin with no error anywhere.
const SingleStoreName = "default"
SingleStoreName is the name the only store answers to in single-store mode.
const TicketHeader = "X-Trove-Ticket"
TicketHeader is the request header that carries an upload ticket. Upload tickets never ride in the URL: forge's tracing records every request's query string, and an upload ticket lives 15 minutes and may allow an overwrite.
Variables ¶
var ( // ErrTicketInvalid reports a ticket that is malformed or whose // signature does not match. ErrTicketInvalid = errors.New("trove/contract: ticket is invalid") // ErrTicketExpired reports a correctly signed ticket past its expiry. ErrTicketExpired = errors.New("trove/contract: ticket has expired") )
Functions ¶
func Register ¶
func Register( d *dispatcher.Dispatcher, reg contract.Registry, wreg contract.WardenRegistry, deps Deps, ) error
Register loads and validates the embedded manifest, registers the `trove` contributor with reg, and binds every handler. A handler bound to an intent the manifest does not declare is a build mistake and fails here rather than at the first request.
Types ¶
type Content ¶
type Content struct {
// Signer mints and checks tickets. Required.
Signer *Signer
// Path is where the content route is mounted, for example
// "/dashboard/trove/content".
Path string
// MaxUploadBytes is the largest upload a ticket will allow. Every
// Trove middleware buffers a whole object in memory, which is why this
// is small by default.
MaxUploadBytes int64
// PerProcessSecret is true when Signer's key was generated at start
// rather than configured, so a ticket verifies only in this process.
PerProcessSecret bool
}
Content is what the content intents and the content route share: the ticket signer, where the route is mounted, and the upload cap.
func (*Content) Handler ¶
Handler serves object content for tickets minted by the content intents. It authorises by ticket signature alone, which is what lets a plain download link work, and it never serves content inline: an uploaded HTML or SVG file would otherwise run on the dashboard's origin as the operator.
type Deps ¶
type Deps struct {
// Stores resolves each intent's `store` field. Required.
Stores *Stores
// Content mints the tickets the content intents return. Required.
Content *Content
// Logger receives an Error entry for every error mapped to
// CodeInternal. Optional.
Logger forge.Logger
}
Deps bundles what the handlers need.
type Flags ¶
Flags records which protections a store's configuration asked for. It is what an operator configured, not what is applied: system.status compares the two.
type Signer ¶
type Signer struct {
// contains filtered or unexported fields
}
Signer mints and checks tickets with one HMAC-SHA256 key.
func NewRandomSigner ¶
NewRandomSigner returns a Signer over a fresh random key. Its tickets verify only in this process, so a deployment with more than one instance needs a configured key instead.
type Stores ¶
type Stores struct {
// contains filtered or unexported fields
}
Stores resolves the optional `store` field every intent takes.
func NewSingleStore ¶
NewSingleStore wraps the one Trove of a single-store deployment. It answers to "" and to SingleStoreName.
func NewStores ¶
NewStores wraps the named Troves of a multi-store deployment, in the order given. An empty defaultName makes the first store the default.
func (*Stores) DefaultName ¶
DefaultName is the store an absent `store` field resolves to.
func (*Stores) Resolve ¶
Resolve maps a request's `store` field to a store. Empty means the default. A value that is blank once trimmed is a bad request: it was sent and cannot be used, and treating it as absent would answer with the default store's objects. An unknown name is not found, never the default.
type Ticket ¶
type Ticket struct {
Store string `json:"s"`
Bucket string `json:"b"`
Key string `json:"k"`
Op string `json:"o"`
Expires int64 `json:"e"`
Subject string `json:"sub,omitempty"`
Size int64 `json:"n,omitempty"`
ContentType string `json:"ct,omitempty"`
Overwrite bool `json:"ow,omitempty"`
Limit int64 `json:"l,omitempty"`
}
Ticket says what one content request may do. A contract intent signs it and the content route checks it, so the route needs nothing from the dashboard's session. Field names are short because the ticket travels in a URL.