controlplane

package
v1.3.0-rc.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 13, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RegisterOpener added in v1.0.1

func RegisterOpener(scheme string, fn Opener)

RegisterOpener registers an opener for the given DSN scheme. Panics on double-register so wiring bugs surface at startup rather than silently overwriting a prior registration.

Types

type AuditEvent

type AuditEvent struct {
	ID        string    `json:"id"`
	At        time.Time `json:"at"`
	TenantID  string    `json:"tenantId"`
	Subject   string    `json:"subject"`
	SessionID string    `json:"sessionId"`
	Transport string    `json:"transport,omitempty"`
	Tool      string    `json:"tool,omitempty"`
	Action    string    `json:"action"`
	Outcome   string    `json:"outcome"`
	// Phase tags two-phase audit records:
	//   "intent"  — pre-handler write (mutation about to happen)
	//   "outcome" — post-handler write (success/failure of the mutation)
	//   ""        — single-shot record (legacy / no phase concept)
	// Older readers that don't recognise the field will see "" via the
	// JSON omitempty fallback and treat the record as legacy single-shot.
	Phase       string            `json:"phase,omitempty"`
	Reason      string            `json:"reason,omitempty"`
	ResourceIDs map[string]string `json:"resourceIds,omitempty"`
	Metadata    map[string]string `json:"metadata,omitempty"`
}

type CredentialRef

type CredentialRef struct {
	ID         string            `json:"id"`
	Backend    string            `json:"backend"`
	Reference  string            `json:"reference"`
	Workspace  string            `json:"workspaceId,omitempty"`
	BaseURL    string            `json:"baseUrl,omitempty"`
	Metadata   map[string]string `json:"metadata,omitempty"`
	ModifiedAt time.Time         `json:"modifiedAt,omitempty"`
}

type DevFileStore added in v1.0.1

type DevFileStore struct {
	// contains filtered or unexported fields
}

DevFileStore is the file/memory-backed implementation of Store. It is the dev/offline fallback; production deployments should register an external backend (Postgres) via RegisterOpener and select it through the DSN scheme. The C1 guard in cmd/clockify-mcp/main.go refuses to start the streamable_http transport against this store unless MCP_ALLOW_DEV_BACKEND=1 is set, because the JSON file-rewrite write pattern and the in-process mutex do not survive a multi-process deployment.

func (*DevFileStore) AppendAuditEvent added in v1.0.1

func (s *DevFileStore) AppendAuditEvent(event AuditEvent) error

func (*DevFileStore) AppendAuditEventBatch added in v1.2.2

func (s *DevFileStore) AppendAuditEventBatch(events []AuditEvent) error

AppendAuditEventBatch appends the slice atomically under the in-memory mutex and rewrites the file once. The auditCap eviction applies to the combined buffer (oldest-first trim) so a large batch on a capped store cannot bypass the cap.

func (*DevFileStore) Close added in v1.0.1

func (s *DevFileStore) Close() error

Close releases backend-owned resources. DevFileStore has no goroutines, no connection pool, and no OS handles held between calls, so Close is a no-op that always returns nil.

func (*DevFileStore) CredentialRef added in v1.0.1

func (s *DevFileStore) CredentialRef(id string) (CredentialRef, bool)

func (*DevFileStore) DeleteSession added in v1.0.1

func (s *DevFileStore) DeleteSession(id string) error

func (*DevFileStore) PutCredentialRef added in v1.0.1

func (s *DevFileStore) PutCredentialRef(record CredentialRef) error

func (*DevFileStore) PutSession added in v1.0.1

func (s *DevFileStore) PutSession(record SessionRecord) error

func (*DevFileStore) PutTenant added in v1.0.1

func (s *DevFileStore) PutTenant(record TenantRecord) error

func (*DevFileStore) RetainAudit added in v1.0.1

func (s *DevFileStore) RetainAudit(ctx context.Context, maxAge time.Duration) (int, error)

RetainAudit drops audit events older than maxAge from the in-memory slice and rewrites the file. Complementary to the WithAuditCap hard-limit: the cap protects against bursty writes between reaper ticks, retention enforces the time window. maxAge <= 0 is a no-op so operators can disable retention by clearing the env var.

func (*DevFileStore) Session added in v1.0.1

func (s *DevFileStore) Session(id string) (SessionRecord, bool)

func (*DevFileStore) Tenant added in v1.0.1

func (s *DevFileStore) Tenant(id string) (TenantRecord, bool)

func (*DevFileStore) UseConfirmationNonce

func (s *DevFileStore) UseConfirmationNonce(ctx context.Context, rec confirmation.ReplayRecord) error

UseConfirmationNonce records a verified confirmation-token nonce. The file-backed implementation is intended for local/dev and small single-node deployments; hosted profiles should use Postgres so replay state is shared.

type Opener added in v1.0.1

type Opener func(dsn string, opts ...Option) (Store, error)

Opener is the factory signature registered by external backends. Modules that ship under a build tag (e.g. `-tags=postgres`) call RegisterOpener from an init() to become dispatchable via Open.

type Option added in v1.0.1

type Option func(*DevFileStore)

Option configures a DevFileStore at construction. External backends parse configuration from DSN query parameters, so Options do not apply to them.

func WithAuditCap added in v1.0.1

func WithAuditCap(n int) Option

WithAuditCap caps the in-memory AuditEvents slice at n entries. Zero or negative disables the cap. See DevFileStore.auditCap for the rationale.

type SessionRecord

type SessionRecord struct {
	ID              string    `json:"id"`
	TenantID        string    `json:"tenantId"`
	Subject         string    `json:"subject"`
	Transport       string    `json:"transport"`
	ProtocolVersion string    `json:"protocolVersion,omitempty"`
	ClientName      string    `json:"clientName,omitempty"`
	ClientVersion   string    `json:"clientVersion,omitempty"`
	CreatedAt       time.Time `json:"createdAt"`
	ExpiresAt       time.Time `json:"expiresAt"`
	LastSeenAt      time.Time `json:"lastSeenAt"`
	WorkspaceID     string    `json:"workspaceId,omitempty"`
	ClockifyBaseURL string    `json:"clockifyBaseUrl,omitempty"`
}

type State

type State struct {
	Tenants            map[string]TenantRecord              `json:"tenants"`
	CredentialRefs     map[string]CredentialRef             `json:"credential_refs"`
	Sessions           map[string]SessionRecord             `json:"sessions"`
	AuditEvents        []AuditEvent                         `json:"audit_events"`
	ConfirmationNonces map[string]confirmation.ReplayRecord `json:"confirmation_nonces,omitempty"`
}

type Store

type Store interface {
	Tenant(id string) (TenantRecord, bool)
	PutTenant(record TenantRecord) error
	CredentialRef(id string) (CredentialRef, bool)
	PutCredentialRef(record CredentialRef) error
	Session(id string) (SessionRecord, bool)
	PutSession(record SessionRecord) error
	DeleteSession(id string) error
	AppendAuditEvent(event AuditEvent) error
	// AppendAuditEventBatch persists a slice of audit events. Semantics
	// are identical to a sequence of AppendAuditEvent calls: same
	// external_id synthesis, same ON CONFLICT-do-nothing dedupe, same
	// per-event error contract. Implementations should consolidate the
	// transport (one round trip on Postgres via pgx.SendBatch) but must
	// not collapse multiple events into a single row. ADR 0022 captures
	// the runtime-layer wrapper that decides which audit events take
	// this path; from the Store's point of view this is just "insert
	// many." An empty slice is a no-op and returns nil.
	AppendAuditEventBatch(events []AuditEvent) error
	// RetainAudit drops audit events older than maxAge and returns the
	// number removed. Called periodically by the retention reaper
	// (B2); maxAge <= 0 is a no-op. Implementations must respect ctx
	// cancellation and must not leave the store in an inconsistent
	// state on partial failure.
	RetainAudit(ctx context.Context, maxAge time.Duration) (int, error)
	// Close releases backend-owned resources (pgxpool, file handles).
	// DevFileStore has nothing to release and returns nil.
	Close() error
}

Store is the durable backend for the control plane. B1 lifted this from a concrete struct to an interface so an external backend (Postgres, behind -tags=postgres) can register alongside the built-in file/memory store. See RegisterOpener for the dispatch mechanism.

func Open

func Open(dsn string, opts ...Option) (Store, error)

Open returns a Store appropriate for the DSN. Built-in schemes: "" (alias for memory), "memory", "memory://", "file://<path>", or a bare filesystem path — all produce a DevFileStore. Any other scheme dispatches to an opener registered via RegisterOpener; if none is registered the error explicitly names the scheme and points the operator at the matching build tag.

type TenantRecord

type TenantRecord struct {
	ID              string            `json:"id"`
	CredentialRefID string            `json:"credentialRefId"`
	WorkspaceID     string            `json:"workspaceId,omitempty"`
	BaseURL         string            `json:"baseUrl,omitempty"`
	Timezone        string            `json:"timezone,omitempty"`
	PolicyMode      string            `json:"policyMode,omitempty"`
	DenyTools       []string          `json:"denyTools,omitempty"`
	DenyGroups      []string          `json:"denyGroups,omitempty"`
	AllowGroups     []string          `json:"allowGroups,omitempty"`
	Metadata        map[string]string `json:"metadata,omitempty"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL