internal/api/manage.go
internal/api · 659 lines · 48 declarations · source
This file carries the package documentation, rendered on the package page.
Declarations
const ManagementPrefix
const ManagementPrefix = "/manage/" + Version
ManagementPrefix is where the management operations live: a prefix of their own, so a proxy or a network policy can expose the memory surface and not this one.
type ManagementStores
type ManagementStores struct {
Projects *pg.ProjectStore
Provision func(ctx context.Context, scope string) error
Observations *pg.ObservationStore
Audit *pg.AuditStore
Refusals *pg.RefusalStore
Eraser *pg.Eraser
// Health is the instance's operational snapshot: the formation backlog, whether a worker is
// answering, and how many connections the deployment holds against the server's ceiling. A
// function for the same reason Provision is — it reads catalogue views the operator connection
// can see and this surface holds no connection of that kind.
//
// Optional: a deployment wired without it loses the panel and keeps every other one, which is
// the right failure for a monitoring read.
Health func(ctx context.Context) (pg.OperationalHealth, error)
}
ManagementStores is what the management surface reads and writes. Provision creates a project's storage and needs the administrative connection; it is a function so the surface holds no connection of that kind itself.
type ManagementServer
type ManagementServer struct {
credentials *credential.Store
stores ManagementStores
schema pg.Schema
log *slog.Logger
admission *Admission
authAudits refusedAuthBudget
}
ManagementServer serves the operator surface.
func NewManagementServer
func NewManagementServer(credentials *credential.Store, stores ManagementStores, schema pg.Schema, log *slog.Logger) *ManagementServer
type managementRoute
type managementRoute struct {
Operation
handler func(*ManagementServer) grantedHandler
request any
response any
}
var managementOperations
var managementOperations = []managementRoute{
{Operation{domain.AuditProjectCreate, http.MethodPost, "/projects/create", http.StatusCreated}, func(m *ManagementServer) grantedHandler { return m.createProject }, projectRequest{}, projectResponse{}},
{Operation{domain.AuditProjectList, http.MethodPost, "/projects/list", http.StatusOK}, func(m *ManagementServer) grantedHandler { return m.listProjects }, nil, projectListResponse{}},
{Operation{domain.AuditProjectSuspend, http.MethodPost, "/projects/suspend", http.StatusOK}, func(m *ManagementServer) grantedHandler { return m.suspendProject }, projectRequest{}, projectResponse{}},
{Operation{domain.AuditProjectResume, http.MethodPost, "/projects/resume", http.StatusOK}, func(m *ManagementServer) grantedHandler { return m.resumeProject }, projectRequest{}, projectResponse{}},
{Operation{domain.AuditProjectRetention, http.MethodPost, "/projects/retention", http.StatusOK}, func(m *ManagementServer) grantedHandler { return m.setProjectRetention }, projectRetentionRequest{}, projectRetentionResponse{}},
{Operation{domain.AuditCredentialIssue, http.MethodPost, "/credentials/issue", http.StatusCreated}, func(m *ManagementServer) grantedHandler { return m.issueCredential }, credentialIssueRequest{}, credentialIssued{}},
{Operation{domain.AuditCredentialList, http.MethodPost, "/credentials/list", http.StatusOK}, func(m *ManagementServer) grantedHandler { return m.listCredentials }, credentialListRequest{}, credentialListResponse{}},
{Operation{domain.AuditCredentialRevoke, http.MethodPost, "/credentials/revoke", http.StatusOK}, func(m *ManagementServer) grantedHandler { return m.revokeCredential }, credentialRevokeRequest{}, credentialRevoked{}},
{Operation{domain.AuditRefusalSummary, http.MethodPost, "/refusals/summary", http.StatusOK}, func(m *ManagementServer) grantedHandler { return m.refusalSummary }, projectPageRequest{}, pg.RefusalSummary{}},
{Operation{domain.AuditErasureList, http.MethodPost, "/erasures/list", http.StatusOK}, func(m *ManagementServer) grantedHandler { return m.listErasures }, projectPageRequest{}, erasureListResponse{}},
{Operation{domain.AuditFormationStatus, http.MethodPost, "/formation/status", http.StatusOK}, func(m *ManagementServer) grantedHandler { return m.formationStatus }, nil, formationStatusResponse{}},
{Operation{domain.AuditFormationParked, http.MethodPost, "/formation/parked", http.StatusOK}, func(m *ManagementServer) grantedHandler { return m.listParked }, parkedRequest{}, pg.ParkedPage{}},
{Operation{domain.AuditFormationUnpark, http.MethodPost, "/formation/unpark", http.StatusOK}, func(m *ManagementServer) grantedHandler { return m.unpark }, unparkRequest{}, unparkResponse{}},
{Operation{domain.AuditAuditSeal, http.MethodPost, "/audit/seal", http.StatusOK}, func(m *ManagementServer) grantedHandler { return m.sealAudit }, nil, domain.AuditSeal{}},
{Operation{domain.AuditAuditVerify, http.MethodPost, "/audit/verify", http.StatusOK}, func(m *ManagementServer) grantedHandler { return m.verifyAudit }, nil, domain.AuditVerification{}},
}
The management surface, declared once, for the same reasons the memory surface is: it has to be enumerable for the contract, for the ledger and for the test that refuses every route to the wrong credential.
func ManagementOperations
func ManagementOperations() []Operation
ManagementOperations is the surface by operation, for the contract and the tests that walk it.
method ManagementServer.Handler
func (m *ManagementServer) Handler(readiness ...http.Handler) http.Handler
Handler mounts every management operation under the prefix, each behind the operator door, and the health endpoints, which say nothing; readiness, when given, is the database answering.
method ManagementServer.operated
func (m *ManagementServer) operated(next grantedHandler) http.Handler
operated is the operator door: an operator credential and nothing else, then the work slot.
method ManagementServer.recordRefusedAuth
func (m *ManagementServer) recordRefusedAuth(r *http.Request)
recordRefusedAuth is the memory door's rule applied here: the presented token never reaches the ledger, and the sampling budget bounds what an anonymous caller can make the instance write.
method ManagementServer.record
func (m *ManagementServer) record(r *http.Request, operation string, grant credential.Grant, project, outcome string, magnitude int)
record puts an operator's operation on the ledger, against the project the request named.
type projectRequest
type projectRequest struct {
Name string `json:"name"`
}
type projectResponse
type projectResponse struct {
Project string `json:"project"`
Suspended bool `json:"suspended"`
}
type projectRetentionRequest
type projectRetentionRequest struct {
Name string `json:"name"`
RetentionDays *int `json:"retention_days"`
}
RetentionDays is how long new turns are kept. Null is indefinite, which is also how a project starts. Days rather than an interval: an interval accepted as text puts a parser between an operator and a deletion schedule.
type projectRetentionResponse
type projectRetentionResponse struct {
Project string `json:"project"`
Retention string `json:"retention"`
}
type projectListResponse
type projectListResponse struct {
Projects []pg.Listed `json:"projects"`
}
method ManagementServer.createProject
func (m *ManagementServer) createProject(w http.ResponseWriter, r *http.Request, grant credential.Grant)
method ManagementServer.listProjects
func (m *ManagementServer) listProjects(w http.ResponseWriter, r *http.Request, grant credential.Grant)
method ManagementServer.suspendProject
func (m *ManagementServer) suspendProject(w http.ResponseWriter, r *http.Request, grant credential.Grant)
method ManagementServer.resumeProject
func (m *ManagementServer) resumeProject(w http.ResponseWriter, r *http.Request, grant credential.Grant)
method ManagementServer.setProjectRetention
func (m *ManagementServer) setProjectRetention(w http.ResponseWriter, r *http.Request, grant credential.Grant)
setProjectRetention says how long a project keeps what it is told from now on.
A governance decision, so it is on the ledger beside suspension, and it changes nothing already stored: a deadline is stamped on a turn when it arrives, and the policy in force then governs it.
func retentionText
func retentionText(days *int) string
retentionText says a policy the way an operator wrote it.
method ManagementServer.setSuspended
func (m *ManagementServer) setSuspended(w http.ResponseWriter, r *http.Request, grant credential.Grant, operation string, suspended bool)
type credentialIssueRequest
type credentialIssueRequest struct {
Name string `json:"name"`
Project string `json:"project"`
// Access is read_only or read_write; omitted means read_write.
Access string `json:"access"`
}
type credentialIssued
type credentialIssued struct {
ID string `json:"id"`
Name string `json:"name"`
Project string `json:"project"`
Access string `json:"access"`
Token string `json:"token"`
}
credentialIssued carries the token once. It is not recoverable afterwards, by anyone.
type credentialListRequest
type credentialListRequest struct {
// Project names whose credentials to list; empty lists the operator credentials.
Project string `json:"project"`
Limit int `json:"limit"`
}
type credentialListResponse
type credentialListResponse struct {
Credentials []credential.Listed `json:"credentials"`
}
type credentialRevokeRequest
type credentialRevokeRequest struct {
ID string `json:"id"`
}
type credentialRevoked
type credentialRevoked struct {
ID string `json:"id"`
Revoked bool `json:"revoked"`
}
method ManagementServer.issueCredential
func (m *ManagementServer) issueCredential(w http.ResponseWriter, r *http.Request, grant credential.Grant)
method ManagementServer.issueProjectCredential
func (m *ManagementServer) issueProjectCredential(ctx context.Context, name, project string, access credential.Access) (string, credential.Grant, error)
issueProjectCredential is the only way a project credential is minted, whichever surface the operator came through. The API and the portal are two ways to reach one set of powers, and a check each caller had to remember is how the portal came to mint credentials the API refused (#51).
The project lives in the memory schema and the credential in the registry. The management server is the one place that sees both, so the reference is checked here rather than by a constraint.
- **Suspended:** a suspended project mints no credential. It would authenticate and reach nothing until somebody resumed the project.
- **Missing:** a name no project uses mints none either. That credential would wait for a project of that name and reach it the moment one was created, which is access nobody meant to grant.
What this does not cover: a project suspended between the check and the insert. The credential then exists for a suspended project, and authentication refuses it until the project is resumed, like every other credential of a suspended project.
method ManagementServer.listCredentials
func (m *ManagementServer) listCredentials(w http.ResponseWriter, r *http.Request, grant credential.Grant)
method ManagementServer.revokeCredential
func (m *ManagementServer) revokeCredential(w http.ResponseWriter, r *http.Request, grant credential.Grant)
type projectPageRequest
type projectPageRequest struct {
Project string `json:"project"`
Limit int `json:"limit"`
}
type erasureListResponse
type erasureListResponse struct {
Erasures []pg.ErasureReceipt `json:"erasures"`
}
method ManagementServer.projectPage
func (m *ManagementServer) projectPage(w http.ResponseWriter, r *http.Request) (projectPageRequest, bool)
method ManagementServer.refusalSummary
func (m *ManagementServer) refusalSummary(w http.ResponseWriter, r *http.Request, grant credential.Grant)
method ManagementServer.listErasures
func (m *ManagementServer) listErasures(w http.ResponseWriter, r *http.Request, grant credential.Grant)
type formationScope
type formationScope struct {
Project string `json:"project"`
Suspended bool `json:"suspended"`
Stored *int64 `json:"stored"`
Formed *int64 `json:"formed"`
Parked int `json:"parked"`
}
type formationStatusResponse
type formationStatusResponse struct {
Scopes []formationScope `json:"scopes"`
}
method ManagementServer.formationStatus
func (m *ManagementServer) formationStatus(w http.ResponseWriter, r *http.Request, grant credential.Grant)
type parkedRequest
type parkedRequest struct {
Project string `json:"project"`
// After is an exclusive log offset; omitted starts from the beginning.
After *int64 `json:"after"`
Limit int `json:"limit"`
}
method ManagementServer.listParked
func (m *ManagementServer) listParked(w http.ResponseWriter, r *http.Request, grant credential.Grant)
type unparkRequest
type unparkRequest struct {
Project string `json:"project"`
ObservationID string `json:"observation_id"`
}
type unparkResponse
type unparkResponse struct {
ObservationID string `json:"observation_id"`
Unparked bool `json:"unparked"`
}
method ManagementServer.unpark
func (m *ManagementServer) unpark(w http.ResponseWriter, r *http.Request, grant credential.Grant)
method ManagementServer.sealAudit
func (m *ManagementServer) sealAudit(w http.ResponseWriter, r *http.Request, grant credential.Grant)
method ManagementServer.verifyAudit
func (m *ManagementServer) verifyAudit(w http.ResponseWriter, r *http.Request, grant credential.Grant)