Skip to main content

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.

source

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.

source

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.

source

func NewManagementServer​

func NewManagementServer(credentials *credential.Store, stores ManagementStores, schema pg.Schema, log *slog.Logger) *ManagementServer

source

type managementRoute​

type managementRoute struct {
Operation
handler func(*ManagementServer) grantedHandler
request any
response any
}

source

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.

source

func ManagementOperations​

func ManagementOperations() []Operation

ManagementOperations is the surface by operation, for the contract and the tests that walk it.

source

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.

source

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.

source

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.

source

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.

source

type projectRequest​

type projectRequest struct {
Name string `json:"name"`
}

source

type projectResponse​

type projectResponse struct {
Project string `json:"project"`
Suspended bool `json:"suspended"`
}

source

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.

source

type projectRetentionResponse​

type projectRetentionResponse struct {
Project string `json:"project"`
Retention string `json:"retention"`
}

source

type projectListResponse​

type projectListResponse struct {
Projects []pg.Listed `json:"projects"`
}

source

method ManagementServer.createProject​

func (m *ManagementServer) createProject(w http.ResponseWriter, r *http.Request, grant credential.Grant)

source

method ManagementServer.listProjects​

func (m *ManagementServer) listProjects(w http.ResponseWriter, r *http.Request, grant credential.Grant)

source

method ManagementServer.suspendProject​

func (m *ManagementServer) suspendProject(w http.ResponseWriter, r *http.Request, grant credential.Grant)

source

method ManagementServer.resumeProject​

func (m *ManagementServer) resumeProject(w http.ResponseWriter, r *http.Request, grant credential.Grant)

source

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.

source

func retentionText​

func retentionText(days *int) string

retentionText says a policy the way an operator wrote it.

source

method ManagementServer.setSuspended​

func (m *ManagementServer) setSuspended(w http.ResponseWriter, r *http.Request, grant credential.Grant, operation string, suspended bool)

source

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"`
}

source

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.

source

type credentialListRequest​

type credentialListRequest struct {
// Project names whose credentials to list; empty lists the operator credentials.
Project string `json:"project"`
Limit int `json:"limit"`
}

source

type credentialListResponse​

type credentialListResponse struct {
Credentials []credential.Listed `json:"credentials"`
}

source

type credentialRevokeRequest​

type credentialRevokeRequest struct {
ID string `json:"id"`
}

source

type credentialRevoked​

type credentialRevoked struct {
ID string `json:"id"`
Revoked bool `json:"revoked"`
}

source

method ManagementServer.issueCredential​

func (m *ManagementServer) issueCredential(w http.ResponseWriter, r *http.Request, grant credential.Grant)

source

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.

source

method ManagementServer.listCredentials​

func (m *ManagementServer) listCredentials(w http.ResponseWriter, r *http.Request, grant credential.Grant)

source

method ManagementServer.revokeCredential​

func (m *ManagementServer) revokeCredential(w http.ResponseWriter, r *http.Request, grant credential.Grant)

source

type projectPageRequest​

type projectPageRequest struct {
Project string `json:"project"`
Limit int `json:"limit"`
}

source

type erasureListResponse​

type erasureListResponse struct {
Erasures []pg.ErasureReceipt `json:"erasures"`
}

source

method ManagementServer.projectPage​

func (m *ManagementServer) projectPage(w http.ResponseWriter, r *http.Request) (projectPageRequest, bool)

source

method ManagementServer.refusalSummary​

func (m *ManagementServer) refusalSummary(w http.ResponseWriter, r *http.Request, grant credential.Grant)

source

method ManagementServer.listErasures​

func (m *ManagementServer) listErasures(w http.ResponseWriter, r *http.Request, grant credential.Grant)

source

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"`
}

source

type formationStatusResponse​

type formationStatusResponse struct {
Scopes []formationScope `json:"scopes"`
}

source

method ManagementServer.formationStatus​

func (m *ManagementServer) formationStatus(w http.ResponseWriter, r *http.Request, grant credential.Grant)

source

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"`
}

source

method ManagementServer.listParked​

func (m *ManagementServer) listParked(w http.ResponseWriter, r *http.Request, grant credential.Grant)

source

type unparkRequest​

type unparkRequest struct {
Project string `json:"project"`
ObservationID string `json:"observation_id"`
}

source

type unparkResponse​

type unparkResponse struct {
ObservationID string `json:"observation_id"`
Unparked bool `json:"unparked"`
}

source

method ManagementServer.unpark​

func (m *ManagementServer) unpark(w http.ResponseWriter, r *http.Request, grant credential.Grant)

source

method ManagementServer.sealAudit​

func (m *ManagementServer) sealAudit(w http.ResponseWriter, r *http.Request, grant credential.Grant)

source

method ManagementServer.verifyAudit​

func (m *ManagementServer) verifyAudit(w http.ResponseWriter, r *http.Request, grant credential.Grant)

source