Add enterprise policy: TOML config, enforcement, and audit logging
Details
Enterprise policy system for headless server deployments:
- EnterprisePolicy: TOML-based config with optional ECDSA P-256 signature
verification to prevent tampering. Supports [plugins], [network], [api],
and [audit] sections with an [authority] signing block.
- PolicyEnforcer: Three enforcement points:
1. Plugin allowlist/blocklist — restricts which plugins can load via
WorkspacePluginConfig's existing whitelist mechanism
2. Network CIDR filtering — ASP.NET Core middleware that blocks requests
from IPs outside allowed CIDR ranges
3. TLS requirement — blocks server startup if policy requires TLS but
it's not configured
- AuditLogger: JSON Lines file writer at admin-controlled path. Logs API
requests (method, path, status, IP, duration) with configurable level
filtering (all/write/auth). Records policy and auth events.
- HeadlessHost: Loads policy before plugin discovery, applies plugin
restrictions, injects network + audit middleware, validates TLS
requirement. --setup-policy flag allows interactive policy configuration.
Add TLS support: manual certificates and Let's Encrypt
Details
LocalApiServer now supports HTTPS via two modes:
1. Manual certificate — load PFX/P12 or PEM+key files directly.
Configured via HeadlessConfig.Tls with mode=Manual.
2. Let's Encrypt (ACME) — automatic free certificate provisioning
via LettuceEncrypt. Requires a public domain and port 80 for
HTTP-01 challenges. Certificates are persisted to disk and
auto-renewed.
Architecture: TlsOptions model lives in PrivStack.Services (shared).
LettuceEncrypt NuGet (1.3.3) is only in PrivStack.Server — the Desktop
never activates it. LocalApiServer exposes OnConfigureServices,
OnConfigureKestrel, and OnConfigureApp hooks so the Server project can
inject LettuceEncrypt without adding the dependency to Services.
Setup wizard now offers both TLS modes interactively. The --setup-tls
flag allows re-configuring TLS independently of the full setup wizard.
Architecture split: extract PrivStack.Services and PrivStack.Server
Details
Phase 0 of the headless server architecture split. Extracts ~155 service
files with zero Avalonia dependency into a shared PrivStack.Services
library and creates a standalone PrivStack.Server headless binary.
Architecture changes:
- New PrivStack.Services project: shared core services, models, native
FFI, AI services, API server, biometric, connections, file sync, IPC,
plugin host, update services — all Avalonia-free
- New PrivStack.Server project: headless console binary (privstack-server)
that references Services + Sdk without any Avalonia dependency
- Desktop now references Services and only contains UI-specific code:
views, view models, theme/font/layout services, dialog service,
backup service, whisper/audio, spell check, plugin registry
Key abstractions created:
- INavigationHost: abstracts MainWindowViewModel navigation for Services
- IEmbeddingService: abstracts ONNX embedding (Desktop implements,
Server stubs)
- IWindowSettingsService: Desktop-only Window position/size persistence
- DesktopAppSettingsService: extends base AppSettingsService with
Avalonia Window operations
- Extensible AI provider registration via constructor injection
Breaking changes to models (Avalonia-free):
- SyncModels: DiscoveryBrush (IBrush) → DiscoveryCategory (string)
- CloudSyncModels: SeverityBrush (IBrush) → SeverityLevel (string)
- IAppSettingsService: removed ApplyToWindow/UpdateWindowBounds
- IDialogService: removed SetOwner/Owner
- IPluginRegistry: SetMainViewModel/GetMainViewModel use object?
Dependency graph:
Sdk (no deps)
↑
Services → Sdk
↑ ↑
Desktop → Services + Sdk + UI.Adaptive + Avalonia
Server → Services + Sdk (no Avalonia)
Add headless API mode (--headless flag)
Details
PrivStack can now run as a headless API server without the Avalonia GUI,
enabling programmatic access via scripts, CI/CD, or background services.
CLI interface:
privstack --headless [--workspace <name>] [--port <N>] [--bind <addr>]
privstack --headless --show-api-key
privstack --headless --generate-api-key
New files:
- HeadlessOptions: parsed CLI arguments record
- HeadlessHost: startup orchestrator (workspace resolution, auth via
PRIVSTACK_MASTER_PASSWORD env var or stdin prompt, plugin discovery,
API server lifecycle, graceful SIGTERM/SIGINT shutdown)
- HeadlessStubs: no-op implementations for 8 UI service interfaces
(dispatcher, dialogs, toast, theme, font scale, layout, notifications,
focus mode)
Modified files:
- Program.cs: Main() returns int exit codes, branches to HeadlessHost
when --headless detected via hand-rolled arg parser
- ServiceRegistration: extracted RegisterCoreServices() + WireCorePostBuild()
shared methods, added ConfigureHeadless() with stub registrations and
cached-password vault unlock
- App.axaml.cs: Services setter changed to internal for HeadlessHost access
- ILocalApiServer/LocalApiServer: added BindAddress property, non-localhost
Kestrel binding support, workspace name/ID in /api/v1/status response
Exit codes: 0=success, 1=config, 2=auth, 3=port-in-use, 4=db-locked
Get notified about new releases