Skip to main content

Platform Module — github.com/opsplanetools/platform

Build once. Import everywhere. The platform module is the shared infrastructure layer that every OpsPlane product imports. Each product focuses on domain logic only — auth, audit, billing, crypto, errors, and middleware are solved once.

Why a Separate Module

Without it, every product rebuilds the same infrastructure:

Arcan:     build auth + audit + billing + crypto + ...     (weeks)
OpsPlane: build auth + audit + billing + crypto + ... (weeks, again)
OpsTools: build auth + audit + billing + ... (weeks, again)
OpsKeys: build auth + audit + billing + ... (weeks, again)

With it, each product is domain logic only:

Platform:  build once, test thoroughly                     (2-3 weeks)
Arcan: import platform → focus on engines, plugins (domain only)
OpsPlane: import platform → focus on AI, intelligence (domain only)
OpsTools: import platform → focus on tool runners (domain only)
OpsKeys: import platform → focus on SSH key management (domain only)

Module Identity

Module:     github.com/opsplanetools/platform
Vanity: (future) platform.opsplane.ai
License: Apache 2.0
Visibility: Public (OSS contributors can see and use it)

Reuse Matrix

PackageArcanOpsPlane.aiOpsToolsSecretVaultOpsKeys
authYesYesYesYesYes
errorsYesYesYesYesYes
auditYesYesYesYesYes
billingEnterprisePro/Team/BizPro $9/moFutureFuture
cryptoYesYesYesYesYes
storeYesYesYesYesYes
middlewareYesYesYesYesYes
healthYesYesYesYesYes
configYesYesYesYesYes
telemetryYesYesYesYesYes

10 packages. 5 products. Zero duplication.

Package Design Principles

Every package in the platform module follows these rules:

  1. Zero product knowledge. The package never references Arcan, OpsPlane, OpsTools, or any specific product. It provides infrastructure, not business logic.

  2. Functional options for configuration. Every constructor uses the WithXxx() pattern. Sensible defaults mean zero-config works, but everything is customizable.

  3. Interfaces for extension. The caller provides implementations (stores, resolvers, sinks). The package provides the engine.

  4. No mandatory dependencies. Each package can be imported independently. auth doesn't require billing. audit doesn't require store. Optional integrations are enabled via WithXxx() options.

  5. Test helpers included. Every package provides mock implementations for testing: auth.MockKeyStore, audit.MockDispatcher, store.MockDB, etc.

Module Structure

github.com/opsplanetools/platform/
├── go.mod
├── go.sum
├── README.md
├── CLAUDE.md # Points to arcan-oss/constitution for standards

├── auth/
│ ├── middleware.go # NewMiddleware + functional options
│ ├── apikey.go # API key generation, hashing, prefix validation
│ ├── jwt.go # JWT validation (JWKS endpoint, static secret)
│ ├── k8s.go # Kubernetes service account auth
│ ├── interfaces.go # KeyStore, UserResolver, TokenClaims
│ ├── options.go # WithAPIKeyPrefix, WithJWKS, WithAnonymous, etc.
│ ├── mock.go # MockKeyStore, MockUserResolver (for tests)
│ └── auth_test.go

├── errors/
│ ├── errors.go # Error types: NotFound, Validation, Forbidden, etc.
│ ├── http.go # WriteJSON — HTTP error response writer
│ ├── cli.go # PrintCLI — terminal error card formatter
│ ├── codes.go # Error code enum and HTTP status mapping
│ └── errors_test.go

├── audit/
│ ├── dispatcher.go # NewDispatcher, Emit, async buffered channel
│ ├── sink_webhook.go # HMAC-signed webhook delivery with retry
│ ├── sink_syslog.go # RFC 5424 syslog (TCP/UDP)
│ ├── interfaces.go # Sink, Store interfaces
│ ├── options.go # WithWebhookSink, WithSyslogSink, WithBufferSize
│ ├── mock.go # MockDispatcher (captures events for assertions)
│ └── audit_test.go

├── billing/
│ ├── service.go # NewService + functional options
│ ├── stripe.go # Stripe API: customers, subscriptions, invoices
│ ├── webhook.go # HandleWebhook — Stripe event processor
│ ├── entitlements.go # HasFeature, CheckQuota, feature gating
│ ├── interfaces.go # LicenseStore, EntitlementChecker
│ ├── options.go # WithStripeKey, WithPlans, WithTrialDays
│ ├── mock.go # MockBillingService (for tests without Stripe)
│ └── billing_test.go

├── crypto/
│ ├── encryptor.go # NewEncryptor, Encrypt/Decrypt (AES-256-GCM)
│ ├── keymanager.go # Key manager factory + HKDF derivation
│ ├── kms_aws.go # AWS KMS envelope encryption
│ ├── kms_gcp.go # GCP Cloud KMS
│ ├── kms_azure.go # Azure Key Vault
│ ├── file.go # File-based key (auto-generate, 0600 permissions)
│ ├── options.go # WithFileKey, WithAWSKMS, WithAutoGenerate
│ ├── mock.go # MockEncryptor (deterministic for tests)
│ └── crypto_test.go

├── store/
│ ├── sqlite.go # NewSQLite — WAL mode, busy timeout, pragmas
│ ├── postgres.go # NewPostgres — connection pool, SSL mode
│ ├── migrate.go # Migration runner (embedded SQL files)
│ ├── helpers.go # SoftDelete, WithDeleted, NowUTC, UUIDv4
│ ├── interfaces.go # DB interface (wraps *sql.DB with helpers)
│ ├── options.go # WithMigrations, WithWAL, WithPoolSize
│ ├── mock.go # MockDB, in-memory SQLite for tests
│ └── store_test.go

├── middleware/
│ ├── requestid.go # X-Request-Id generation/extraction
│ ├── logger.go # Structured slog request/response logging
│ ├── ratelimit.go # Per-IP and per-token rate limiting
│ ├── cors.go # Configurable CORS headers
│ ├── recoverer.go # Panic → 500 with request_id in response
│ ├── tls.go # TLS auto-setup (self-signed if not configured)
│ └── middleware_test.go

├── health/
│ ├── checker.go # NewChecker, WithCheck, Handler
│ ├── mock.go # MockChecker
│ └── health_test.go

├── config/
│ ├── loader.go # Load — flags > env > file > defaults
│ ├── options.go # WithEnvPrefix, WithFile, WithDefaults
│ └── config_test.go

└── telemetry/
├── logging.go # Slog handler setup (text vs JSON)
├── metrics.go # Prometheus counter/histogram helpers
├── context.go # RequestContext, typed context keys, propagation
└── telemetry_test.go

Usage Examples

Arcan Server

package main

import (
"github.com/opsplanetools/platform/auth"
"github.com/opsplanetools/platform/audit"
"github.com/opsplanetools/platform/config"
"github.com/opsplanetools/platform/crypto"
"github.com/opsplanetools/platform/errors"
"github.com/opsplanetools/platform/health"
"github.com/opsplanetools/platform/middleware"
"github.com/opsplanetools/platform/store"
"github.com/opsplanetools/platform/telemetry"
)

func main() {
// Config
cfg, _ := config.Load(
config.WithEnvPrefix("ARCAN"),
config.WithFile("~/.arcan/config.yaml"),
)

// Logging
telemetry.SetupLogging(cfg.String("log.format")) // "text" or "json"

// Database
db, _ := store.NewSQLite(cfg.String("store.path"),
store.WithMigrations(arcanMigrations),
store.WithWAL(true),
)

// Encryption
enc, _ := crypto.NewEncryptor(
crypto.WithFileKey("~/.arcan/master.key"),
crypto.WithAutoGenerate(true),
)

// Auth
authMW := auth.NewMiddleware(
auth.WithAPIKeyPrefix("arc_"),
auth.WithAPIKeyStore(db), // db implements auth.KeyStore
)

// Audit
dispatcher := audit.NewDispatcher(
audit.WithStoreSink(db),
audit.WithProductName("arcan"),
)

// Health
hc := health.NewChecker(
health.WithVersion(version, commit, built),
health.WithMode("standalone"),
health.WithCheck("database", db.HealthCheck),
health.WithCheck("encryption", enc.HealthCheck),
)

// Router
r := chi.NewRouter()
r.Use(middleware.RequestID())
r.Use(middleware.Logger(slog.Default()))
r.Use(middleware.RateLimit(100))
r.Use(authMW.Handler)

r.Get("/api/v1/health", hc.Handler)

// ... Arcan-specific handlers (KV, engines, plugins)
}

OpsPlane Server

func main() {
cfg, _ := config.Load(
config.WithEnvPrefix("OPSPLANE"),
config.WithFile("~/.opsplane/config.yaml"),
)

db, _ := store.NewPostgres(cfg.String("database.url"),
store.WithMigrations(opsplaneMigrations),
store.WithPoolSize(20),
)

authMW := auth.NewMiddleware(
auth.WithAPIKeyPrefix("ops_"),
auth.WithAPIKeyStore(db),
auth.WithJWKS("https://clerk.example.com/.well-known/jwks.json"),
)

billing := billing.NewService(
billing.WithStripeKey(os.Getenv("STRIPE_SECRET_KEY")),
billing.WithPlans(opsplanePlans),
billing.WithLicenseStore(db),
)

// ... OpsPlane-specific handlers (AI, intelligence, dashboards)
}

OpsTools Server

func main() {
cfg, _ := config.Load(
config.WithEnvPrefix("OPSTOOLS"),
)

authMW := auth.NewMiddleware(
auth.WithSupabaseJWT(cfg.String("supabase.project"), cfg.String("supabase.jwt_secret")),
auth.WithAnonymous(true), // tools work without auth
)

billing := billing.NewService(
billing.WithStripeKey(os.Getenv("STRIPE_SECRET_KEY")),
billing.WithPlans(map[string]billing.Plan{
"pro": {PriceID: "price_xxx", Features: []string{"ai-tools"}},
}),
)

// ... OpsTools-specific handlers (tool runners, Bedrock integration)
}

Interface Contracts

Each package defines interfaces that callers implement. This is how the platform stays product-agnostic:

// auth/interfaces.go
type KeyStore interface {
LookupKeyHash(ctx context.Context, hash string) (*KeyRecord, error)
}

type UserResolver interface {
ResolveUser(ctx context.Context, subject string) (*User, error)
}

// audit/interfaces.go
type Sink interface {
Send(ctx context.Context, event Event) error
Name() string
}

type Store interface {
InsertAuditEvent(ctx context.Context, event Event) error
}

// billing/interfaces.go
type LicenseStore interface {
GetLicense(ctx context.Context, id string) (*License, error)
UpdateLicense(ctx context.Context, license *License) error
GetActivation(ctx context.Context, fingerprint string) (*Activation, error)
CreateActivation(ctx context.Context, activation *Activation) error
}

type EntitlementChecker interface {
HasFeature(ctx context.Context, userID string, feature string) (bool, error)
}

Every product's database layer implements these interfaces. The platform never imports the product — the product imports the platform.

Testing

Every package includes mock implementations:

// In product tests:
import "github.com/opsplanetools/platform/auth"

func TestMyHandler(t *testing.T) {
mockKeys := auth.NewMockKeyStore()
mockKeys.AddKey("arc_test123", "user-1", []string{"secrets:read"})

authMW := auth.NewMiddleware(
auth.WithAPIKeyPrefix("arc_"),
auth.WithAPIKeyStore(mockKeys),
)

// test handler with real auth middleware, mock store
}
import "github.com/opsplanetools/platform/audit"

func TestAuditEmission(t *testing.T) {
mock := audit.NewMockDispatcher()

// ... trigger action ...

events := mock.Events()
assert.Len(t, events, 1)
assert.Equal(t, "secret.created", events[0].Type)
}
import "github.com/opsplanetools/platform/store"

func TestMigrations(t *testing.T) {
db := store.NewMockDB(t) // in-memory SQLite with test cleanup
// ... test store operations
}

Versioning

Module versioning: semver (v0.1.0, v0.2.0, v1.0.0)

Rules:
- Adding new options (WithXxx) → minor version bump
- Adding new packages → minor version bump
- Adding new interface methods → MAJOR version bump (breaks implementers)
- Changing function signatures → MAJOR version bump
- Bug fixes → patch version bump

All products pin to a specific version:
require github.com/opsplanetools/platform v0.3.2

Products upgrade on their own schedule — platform releases don't force product releases.

Development Workflow

1. Need a new platform feature?
→ Open PR on github.com/opsplanetools/platform
→ Tests pass, review, merge
→ Tag new version

2. Product wants the feature?
→ go get github.com/opsplanetools/[email protected]
→ Use the new package/option
→ No other product is affected

3. Breaking change needed?
→ Major version bump
→ Products upgrade when ready
→ Old version continues working