Viking CMS — historical project plan
Historical design, not current instructions. This document preserves the initial proposal and its section numbering. Several proposals were superseded: authentication is built into the CMS (no Basic auth or ingress auth adapter), both PVCs use RWX, themes/WordPress/plugins are implemented, and the project license is Apache-2.0 under LICENCE, not the proposed MIT license below. Use current contracts, installation and current status for supported behaviour. Unimplemented proposals here are not commitments or available commands. All active open work is tracked in TODO.md; completed or superseded proposals below remain only as historical context.
Project: Viking CMS
Owner: TLSPU Limited
Status: Initial architecture and implementation plan
Version: 0.1.0-draft
License: MIT (proposed; confirm before publication)
Repository: Single Git repository (monorepo)
Primary platform: Kubernetes
Preferred deployment: TAK-OS Orbit Deploy
Preferred ingress: TAK-OS Ingress
1. Executive Summary
Viking CMS is a Kubernetes-native visual content management and static website hosting platform developed by TLSPU Limited.
Its purpose is to provide an alternative to traditional web hosting stacks, including Apache/PHP-based hosting systems and WordPress, without sacrificing the ease of use expected by nontechnical website owners.
Viking CMS separates website creation, content management, publication and delivery into independent components.
The public-facing website is served by an extremely lightweight, high-performance HTTP server. It does not execute application code, query databases, communicate with the CMS, or require access to private website content.
Website owners manage content through an authenticated, browser-based visual CMS. The CMS generates complete static websites and publishes them to shared storage using an atomic publication mechanism.
Kubernetes Custom Resources define websites and their required infrastructure. A dedicated operator provisions and maintains the corresponding Kubernetes resources.
Viking CMS is a standalone TLSPU Limited product.
It integrates naturally with the TAK-OS ecosystem but must operate on ordinary Kubernetes clusters without mandatory TAK-OS dependencies.
1.1 Core principles
- Kubernetes-native: Website infrastructure is declared through Kubernetes Custom Resources.
- Static serving: Public-facing servers serve files only.
- Separation of concerns: Authoring, publishing, storage, routing and serving remain independent.
- Security by design: The public-facing serving process has minimal privileges and no access to private CMS data.
- Visual editing: Website owners should not require programming, template-language or Kubernetes knowledge.
- Independent scaling: Each website has its own Deployment and can scale independently.
- Atomic publishing: Visitors must not encounter partially generated websites.
- Interchangeable components: TAK-OS integrations are preferred but optional.
- Small footprint: Runtime images and resource requirements should be minimised.
- Open source: The project should be suitable for public development and independent adoption.
- Declarative operation: Infrastructure changes are reconciled by the operator rather than applied manually.
- No unnecessary complexity: Avoid introducing databases, message queues, frameworks or distributed services without a demonstrated requirement.
1.2 Non-goals
Viking CMS is not intended to:
- Execute PHP, CGI, Python or arbitrary application code on public requests.
- Provide a general-purpose dynamic application hosting platform.
- Replace Kubernetes networking or ingress controllers.
- Provide DNS hosting or DNS zone management.
- Provide SMTP, IMAP or mailbox hosting.
- Require TAK-OS.
- Require a database cluster.
- Require Helm.
- Provide a WordPress plugin compatibility layer.
- Execute arbitrary user-supplied code in the public serving process.
- Reimplement an entire general-purpose web server such as Apache or Nginx.
These capabilities may be provided by independent external services where necessary.
2. Technology and Development Standards
2.1 Repository structure
The entire project shall be maintained in one Git repository.
This includes:
- Kubernetes operator
- Website CRD definitions
- Static HTTP server
- CMS backend
- Visual editor integration
- Publishing engine
- Deployment manifests
- Container build definitions
- Tests
- Documentation
- GitLab CI configuration
Components may produce separate container images and have independent release lifecycles, but their source code shall remain in the same repository.
Proposed structure
viking-cms/
|- cms/ - All CMS source and Dockerfile here
|- controller - All Controller source and Dockerfile here.
|- frontend - All frontend source and Dockerfile here. This is the highspeed flatfile webserver
|- deploy/ - Sample K8S manifests ready to apply with kubectl
|- orbit-deployments/default/ - Manifests in Orbit Deploy format with deployment.json files etc. This is a symlink created manually only in this development environment (not checked into this repo) to ease updating the production deployment manifests
|- README.md - Agents to keep this updated as a simple, very high level project overview
|- docs/ - Detailed project documentation layed out in a logical structure, ready to be rendered into a flat website
|- docs/planning/project-plan.md - This file - The description of what we are trying to create.
|- LICENSE - Apache 2.0
|- .gitlab-ci.yml - CI
Each of the key components will have its own tests folder, containing tests appropriate to its function and structure. They shall be called in test jobs at stage test in .gitlab-ci.yml
This is an initial layout and may evolve as implementation progresses.
2.2 Go development requirements
Go is the preferred implementation language for:
- Kubernetes operator
- Static HTTP server
- CMS backend API
- Publishing engine, if implemented as a separate executable
All Go applications MUST be compiled with:
CGO_ENABLED=0
All Go runtime containers MUST use:
FROM scratch
This is a mandatory project requirement, not a recommendation.
Go applications must be statically compiled and must not depend on a distribution userspace.
Recommended build flags:
CGO_ENABLED=0 GOOS=linux \
go build -trimpath -ldflags="-s -w"
Architecture-specific builds must set the appropriate GOARCH.
The build system should support at least:
- linux/amd64
- linux/arm64
Other architectures may be added later.
Go container requirements
Go containers must:
- Use multistage builds.
- Contain the application executable and only essential runtime files.
- Run as a non-root numeric UID/GID.
- Require no shell.
- Require no package manager.
- Require no dynamically linked libraries.
- Use a read-only root filesystem wherever possible.
- Avoid writing temporary files unless explicitly necessary.
- Write application logs to stdout/stderr.
- Support graceful termination through Kubernetes signals.
Where outbound TLS connections require CA certificates, copy the required CA bundle from the build stage into the scratch image.
Example:
FROM golang:1.26-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux \
go build -trimpath -ldflags="-s -w" \
-o /application ./cmd/application
FROM scratch
COPY --from=builder /application /application
COPY --from=builder /etc/ssl/certs/ca-certificates.crt \
/etc/ssl/certs/ca-certificates.crt
USER 65532:65532
ENTRYPOINT ["/application"]
The builder image must be pinned to a supported Go version and preferably an immutable digest in production CI.
The example assumes the builder contains a populated CA bundle; the build must install or generate it if necessary.
Certificates should only be copied when the application needs them.
2.3 Other languages
Where another language is more appropriate, such as JavaScript/TypeScript for the visual editor, its runtime container MUST use an Alpine Linux base.
Exceptions require an explicit architectural decision and documentation.
Frontend assets may be compiled during CI and embedded into a Go executable or copied into its scratch image.
A JavaScript build toolchain does not imply that Node.js must be present in the production container.
The CMS frontend should preferably be compiled to static assets.
2.4 Dependencies
Dependencies should be:
- Actively maintained.
- Open source where practical.
- Compatible with the project's licensing.
- Minimized.
- Version pinned.
- Scanned during CI.
Prefer Go standard-library functionality where it provides acceptable performance and maintainability.
Do not introduce dependencies solely for developer convenience when their functionality is trivial to implement safely.
2.5 CI/CD
GitLab CI is the primary build and release system.
CI must include:
- Source formatting checks.
- Static analysis and linting.
- Unit tests.
- Integration tests.
- Kubernetes manifest validation.
- Dependency vulnerability scanning.
- Container image scanning.
- Container build verification.
- Multi-architecture builds.
- Release artifact generation.
Tagged releases should produce:
- Versioned container images for each runtime component.
- Multi-architecture image manifests.
- Kubernetes CRD manifests.
- Installation manifests.
- Orbit Deploy installation examples.
- Release notes.
- Checksums and provenance metadata where supported.
Container images must be tagged with both the release version and immutable digest.
Releases should be reproducible where practical.
3. System Architecture
3.1 Major components
Viking CMS consists of four primary components.
Viking Website Operator
A Kubernetes controller responsible for reconciling Website custom resources into Kubernetes infrastructure.
Viking Webserver
A high-performance static HTTP server responsible exclusively for serving published website files.
Viking CMS
An authenticated, browser-based visual content management system.
Viking Publisher
The component responsible for rendering, validating and atomically publishing completed websites.
The publisher may initially be integrated into the CMS backend but must retain a clearly defined internal interface.
3.2 Logical architecture
Kubernetes API
|
Website CRD
|
v
+------------------+
| Viking Operator |
+------------------+
|
Reconciles resources
|
+------------------+------------------+
| |
v v
PUBLIC SERVING STACK PRIVATE CMS STACK
+--------------------+ +--------------------+
| Public Ingress | | Authenticated |
| TAK-OS or standard | | CMS Ingress |
+--------------------+ +--------------------+
| |
v v
+--------------------+ +--------------------+
| ClusterIP Service | | ClusterIP Service |
+--------------------+ +--------------------+
| |
v v
+--------------------+ +--------------------+
| Webserver | | Visual CMS |
| Deployment | | Deployment |
| Initial replicas:2 | | Initial replicas:1 |
+--------------------+ +--------------------+
| |
READ ONLY READ/WRITE
| |
v v
+--------------------+ +--------------------+
| Published RWX PVC |<---Publish-----| Private CMS PVC |
| Static files only | | Drafts/Templates |
+--------------------+ +--------------------+
The CMS publisher writes completed releases to the published RWX PVC.
The public webserver must not access the CMS API, private PVC, drafts, credentials or publishing controls.
3.3 Deployment environments
Viking CMS must support:
TAK-OS
Preferred environment.
- Orbit Deploy for installation and lifecycle management.
- TAK-OS Ingress for HTTP routing and TLS.
- Standard Kubernetes storage interfaces.
Kubernetes with TAK-OS components
Individual TAK-OS components may be used independently.
For example:
- Standard Kubernetes + Orbit Deploy.
- Standard Kubernetes + TAK-OS Ingress.
- Standard Kubernetes + both.
Plain Kubernetes
The complete core product must work without TAK-OS components.
Standard Kubernetes Ingress resources shall be supported.
Ingress-specific authentication or advanced features may require a compatible controller or additional supporting components.
The operator must report unsupported functionality rather than silently deploying an insecure or incomplete configuration.
4. Website Custom Resource
4.1 API
Initial API:
apiVersion: cms.tlspu.com/v1alpha1
kind: Website
The final API group should be confirmed before the first public release.
The Website resource is namespace-scoped.
Each Website represents one independently managed public website and its associated CMS.
4.2 Proposed specification
apiVersion: cms.tlspu.com/v1alpha1
kind: Website
metadata:
name: example
namespace: websites
spec:
domain: example.com
aliases:
- www
- shop
- blog.example.net
cms:
hostname: cms.example.com
authentication:
oauth2:
clientSecretRef:
name: example-cms-oauth2
storage:
published:
size: 5Gi
storageClassName: cephfs
private:
size: 2Gi
storageClassName: standard
publishing:
retainReleases: 0
frontend:
replicas: 2
ingress:
className: tak-os
This is the proposed initial schema, subject to implementation validation.
4.3 Domain and aliases
spec.domain defines the primary website hostname.
spec.aliases contains alternate hostnames.
Alias resolution rules:
- An alias containing no dots is relative to the primary domain.
- An alias containing dots is treated as an absolute hostname.
- A dotted alias must not duplicate the primary domain or be a subdomain of it.
- Relative aliases must not contain invalid DNS label characters.
- Duplicate effective hostnames are rejected.
- Hostnames must be validated and normalized consistently.
- Wildcard hostnames are not supported initially.
Example:
domain: example.com
aliases:
- www
- shop
- blog.example.net
Produces:
example.com
www.example.com
shop.example.com
blog.example.net
The operator must detect hostname conflicts with other Website resources under its management.
4.4 CMS hostname
spec.cms.hostname defines the externally accessible CMS hostname.
It must not conflict with any public website hostname.
The CMS hostname is always protected by authentication.
The CMS hostname must not be exposed through an unauthenticated ingress.
4.5 Authentication
The CMS supports exactly one configured ingress authentication mechanism.
Initial supported mechanisms:
- OAuth2/OIDC
- HTTP Basic Authentication
Credentials must be stored in Kubernetes Secrets, not plaintext Website fields.
OAuth2/OIDC
Example:
cms:
hostname: cms.example.com
authentication:
oauth2:
clientSecretRef:
name: example-oauth2
OAuth2 integration must account for:
- Identity provider / issuer URL.
- Client ID.
- Client secret.
- Redirect URI.
- Session management.
- Cookie security.
- Logout behaviour.
- CSRF protection.
- TLS requirements.
These values may be provided through operator-level defaults and per-Website references.
The authentication adapter must support TAK-OS Ingress where possible and provide a documented portable alternative for other ingress controllers.
Basic authentication
Example:
cms:
hostname: cms.example.com
authentication:
basic:
secretRef:
name: example-basic-auth
The referenced Secret should contain credentials in a format appropriate for the selected authentication adapter.
Password hashes are preferred over plaintext password storage.
Basic authentication must only be exposed over HTTPS.
Authentication validation
The CRD and operator must reject:
- Multiple authentication mechanisms.
- Missing required Secret references.
- Invalid authentication configuration.
- Unsupported authentication providers.
- Configurations that would expose the CMS without effective authentication.
The operator must not silently fall back to unauthenticated access.
4.6 Storage
Each Website requires two persistent volumes.
Published content PVC
- Access mode: ReadWriteMany.
- Mounted read-only in public webserver Pods.
- Mounted read-write by the publisher.
- Contains only published website assets and release metadata.
- No drafts, credentials, database files or private CMS state.
Private CMS PVC
- Access mode: ReadWriteMany (initially ReadWriteOnce), so backup jobs and importers run as separate microservices that mount it concurrently with the CMS.
- Mounted by the CMS.
- Contains editable project data, templates, media, drafts and settings.
- Not mounted by the public webserver.
The storage classes and capacities must be configurable.
PVC expansion should be supported where the storage provider permits it.
PVC deletion must be conservative and explicitly controlled.
4.7 Frontend replicas
Default:
frontend:
replicas: 2
Two replicas provide capacity for rolling restarts without planned downtime, subject to successful readiness checks and sufficient cluster capacity.
The operator should use:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
The operator should configure topology spreading where practical.
Independent HPA support is planned.
Initial implementation may use fixed replica counts.
4.8 Website status
The operator should expose:
status:
observedGeneration: 1
conditions:
- type: Ready
status: "True"
reason: ComponentsReady
- type: FrontendReady
status: "True"
reason: Available
- type: CMSReady
status: "True"
reason: Available
- type: StorageReady
status: "True"
reason: Bound
- type: Published
status: "True"
reason: ReleaseAvailable
currentRelease: "000001"
Status conditions must reflect observed state, not merely successful resource creation.
5. Website Operator
5.1 Responsibilities
The operator watches Website resources and reconciles their desired state.
For each Website, it creates or maintains:
Public frontend
- Ingress
- ClusterIP Service
- Deployment
- RWX PVC
- Required configuration
- ServiceAccount
- NetworkPolicy, where supported
CMS backend
- Authenticated Ingress
- ClusterIP Service
- CMS Deployment
- Private PVC
- Authentication integration
- Required configuration
- ServiceAccount
- NetworkPolicy, where supported
Optional resources
- HorizontalPodAutoscaler
- PodDisruptionBudget
- Additional monitoring configuration
5.2 Reconciliation requirements
Reconciliation must be:
- Idempotent.
- Safe to retry.
- Resistant to partial failures.
- Based on declared desired state.
- Observable through Kubernetes events and status conditions.
The operator must detect and handle:
- Missing PVCs.
- Failed volume provisioning.
- Invalid hostnames.
- Hostname collisions.
- Missing Secrets.
- Deployment failures.
- Failed ingress configuration.
- Insufficient replica availability.
- Resource deletion.
- Configuration changes.
- Unsupported ingress features.
5.3 Ownership and deletion
Generated resources should use Kubernetes owner references where appropriate.
Persistent data must not be automatically destroyed simply because a Website resource is deleted.
Default policy:
retentionPolicy: Retain
Retention and cleanup behaviour must be documented.
The operator should use finalizers where necessary to coordinate safe cleanup.
5.4 Operator permissions
RBAC must follow least privilege.
The operator should only receive the permissions required to reconcile supported resources.
It must not require cluster-admin privileges during normal operation.
Cluster-scoped installation permissions for CRDs and controller RBAC should be separated from runtime permissions.
5.5 Namespace boundaries
Website resources are namespace-scoped.
The generated serving and CMS workloads must reside in the namespace containing their PVCs.
The initial implementation may deploy multiple Websites into one namespace, but must not assume that all Websites share a namespace.
Cross-namespace PVC mounting is not supported.
6. Static Webserver
6.1 Purpose
The Viking Webserver is a small, high-performance, file-only HTTP server.
It has no knowledge of:
- CMS implementations.
- Kubernetes.
- Website Custom Resources.
- Databases.
- Authentication providers.
- Domain ownership.
- Publishing workflows.
- Other websites.
Each website receives its own Deployment.
Ingress routing determines which Deployment receives each request.
6.2 Language selection
Candidate languages:
- Go
- Rust
Go is the initial preference because of implementation simplicity, ecosystem maturity and straightforward static compilation.
Potential Go implementations:
- Standard
net/http fasthttp
The final choice must be based on benchmarks using realistic static-file workloads.
Performance should be evaluated against Apache HTTPD with a minimal configuration.
The design must not assume that fasthttp is faster for all workloads.
6.3 Runtime interface
The server should require minimal configuration.
Example:
viking-webserver \
--root=/www/current \
--listen=:8080
Configuration may be provided through flags or environment variables.
The server must not require a Kubernetes API connection.
6.4 HTTP features
Required:
- HTTP/1.1 serving.
- GET requests.
- HEAD requests.
- Correct status codes.
- MIME type handling.
- Configurable index files.
- 404 responses.
- 405 responses for unsupported methods.
- ETag support.
- Last-Modified support.
- Conditional requests.
- HTTP Range requests.
- Cache-Control headers.
- Safe URL/path handling.
- Graceful shutdown.
- Health and readiness endpoints.
Optional:
- Precompressed gzip assets.
- Precompressed Brotli assets.
- Custom error pages.
- SPA fallback.
- Configurable directory redirects.
Not supported:
- CGI.
- PHP.
- Server-side scripting.
- Dynamic template execution.
- Database queries.
- Arbitrary URL rewriting.
- General-purpose reverse proxying.
6.5 Security
The webserver must:
- Run as non-root.
- Use a
scratchcontainer. - Have a read-only root filesystem.
- Drop all Linux capabilities.
- Disable privilege escalation.
- Use a restrictive seccomp profile.
- Disable automatic Kubernetes ServiceAccount token mounting.
- Have no access to the private CMS PVC.
- Have no access to Kubernetes Secrets unless specifically justified.
- Avoid outbound network access.
- Prevent directory traversal.
- Prevent unintended symlink traversal.
- Never expose files outside the configured public document root.
The webserver should not require a writable temporary directory.
Unknown paths must not expose internal filesystem details.
6.6 Performance
The server should make effective use of Linux filesystem caching.
Initial implementation should rely on kernel page caching rather than introducing a custom application-level cache.
Benchmark workloads should include:
- Small HTML files.
- CSS and JavaScript assets.
- Images.
- Large file downloads.
- Repeated cached requests.
- High concurrency.
- Mixed file sizes.
- Cold filesystem cache.
- Warm filesystem cache.
Metrics should include:
- Requests per second.
- Latency percentiles.
- CPU usage.
- Memory usage.
- Network throughput.
- Filesystem read latency.
- Error rates.
The previously observed TAK-OS Ingress throughput of over 93,000 requests per second during a security researcher's attack run provides useful context, but is not a validated Viking CMS performance target.
6.7 Health endpoints
Suggested:
/healthz
/readyz
/metrics
Health and metrics endpoints should preferably be exposed on a separate internal listener or otherwise protected from public access.
Readiness must reflect the availability of the active published content.
The server should return an appropriate unavailable response when no valid publication exists.
7. Visual CMS
7.1 Purpose
The CMS must provide an approachable, visual website creation and editing experience.
The target user is someone comfortable with WordPress-style editing but without programming knowledge.
Users must not need to understand:
- Kubernetes.
- HTML.
- CSS.
- JavaScript.
- Go templates.
- Hugo templates.
- YAML.
- Git.
- Container images.
- Deployment pipelines.
7.2 Initial editor candidate
GrapesJS Open Source is the initial candidate for the visual editing engine.
This is not a final technology decision.
The editing experience, extensibility, export behaviour and licensing must be evaluated before adoption.
Other visual editors may be considered.
7.3 CMS features
Initial target features:
- Browser-based editing.
- Visual page layout.
- Inline text editing.
- Drag-and-drop components.
- Page creation and deletion.
- Page navigation.
- Image upload.
- Media library.
- Reusable page components.
- Site-wide header and footer.
- Responsive preview.
- Desktop/tablet/mobile preview.
- Draft saving.
- Publication preview.
- Publish action.
- Basic site settings.
- SEO page titles and descriptions.
- Sitemap generation.
- robots.txt generation.
Future candidates:
- Blog collections.
- Categories and tags.
- Reusable content templates.
- Scheduled publishing.
- Multiple editors.
- Role-based permissions.
- Contact forms.
- Theme marketplace.
- Import from existing websites.
- WordPress migration tooling.
7.4 User experience principles
The CMS should prioritise content editing over technical configuration.
Users should be able to:
- Open the CMS.
- Select a page.
- Click content to edit it.
- Add images or components.
- Preview changes.
- Save a draft.
- Publish.
Advanced design controls should not obstruct ordinary editing tasks.
Where practical, distinguish between:
- Website design.
- Routine content editing.
7.5 CMS storage
The CMS initially uses ordinary filesystem storage.
No database is required for the MVP.
The private PVC may contain:
/cms/
??? project/
? ??? project.json
? ??? pages/
? ??? settings.json
? ??? components/
?
??? assets/
??? templates/
??? drafts/
??? working/
The exact format depends on the selected editor.
Editable project state must be preserved independently of generated HTML.
Generated output must not be treated as the authoritative editable source.
7.6 CMS runtime
The CMS backend may be written in Go.
The browser editor may use JavaScript or TypeScript.
Compiled browser assets should be served by the Go CMS backend where practical.
The Go CMS backend must use CGO_ENABLED=0 and a scratch runtime container.
If another runtime language is selected, its runtime image must use Alpine Linux.
7.7 CMS security
The CMS must:
- Be protected by the configured ingress authentication.
- Validate all file uploads.
- Restrict filesystem access.
- Protect against path traversal.
- Apply appropriate CSRF protections.
- Validate publication requests.
- Avoid exposing private project files.
- Restrict access to publishing operations.
- Use secure session handling.
- Avoid arbitrary command execution.
Authentication at ingress is an access boundary, but the CMS must still treat browser input as untrusted.
8. Publishing Engine
8.1 Purpose
The publisher transforms editable CMS content into a complete static website.
Publication is independent of public request handling.
Rendering speed is not a critical performance requirement.
A publication may take seconds or minutes without affecting the currently published website.
8.2 Publication process
The publisher must:
- Save or snapshot the current editable project state.
- Allocate a new release directory.
- Generate all required HTML.
- Generate CSS and JavaScript assets.
- Copy or transform media.
- Generate metadata and sitemap files.
- Validate the completed release.
- Atomically activate the new release.
- Update publication metadata.
- Clean up obsolete releases according to retention policy.
The active release must never be modified in place.
8.3 Directory structure
Proposed:
/www/
??? releases/
? ??? 000001/
? ??? 000002/
? ??? 000003/
?
??? current -> releases/000003
Each release directory is immutable once published.
8.4 Atomic activation
The publisher creates a new symbolic link pointing to the completed release.
Example:
ln -s releases/000003 current.new
mv -Tf current.new current
The replacement of the current symlink must be implemented using an atomic rename operation on the same filesystem.
The actual implementation must not depend on shell utilities being available inside the runtime container.
For Go implementations, use the appropriate filesystem APIs.
The implementation must verify the semantics of the selected RWX storage provider.
A pair of ordinary directory renames must not be treated as a single atomic transaction.
8.5 Publication consistency
The server must resolve the active publication correctly.
The design must avoid permanently caching an obsolete symlink target.
Kubernetes subPath mounts must not pin the server to an obsolete release.
Old releases must remain available long enough for in-flight requests to finish.
Versioned asset URLs or content-hashed asset names are preferred to avoid mixed-release page loads.
8.6 Release retention
Website configuration:
publishing:
retainReleases: 0
Default:
0
Meaning:
0: No historical releases retained after safe cleanup.1: Retain one previous release.N: Retain N previous releases.
Retention is distinct from temporary cleanup delays required for safe publication.
Rollback history is not a replacement for storage backups.
8.7 Rollback
Where retained releases exist, the CMS should provide a rollback operation.
Rollback changes the active release pointer.
Rollback must not require rerendering the website.
The publisher should record:
- Release ID.
- Publication timestamp.
- Source project revision or snapshot identifier.
- Publication result.
- Optional description.
- Previous release ID.
8.8 Failure handling
If rendering fails:
- The current release remains active.
- The failed release is not published.
- The CMS reports the failure.
- Temporary files are cleaned up safely.
- Logs identify the failure.
If activation fails:
- The existing publication remains available where filesystem guarantees permit.
- The publisher reports an error.
- The operation must be safely retryable.
Concurrent publication requests must be serialized or otherwise coordinated to prevent conflicting activations.
9. Ingress Integration
9.1 General requirements
Viking CMS must use standard Kubernetes networking resources wherever possible.
The operator creates:
- Public website Ingress.
- CMS Ingress.
TLS termination occurs at the ingress controller.
The webserver and CMS may communicate with ingress over internal HTTP.
9.2 TAK-OS Ingress
TAK-OS Ingress is the preferred implementation.
Where available, Viking CMS may use TAK-OS-specific capabilities for:
- Authentication integration.
- Advanced routing.
- Security policy.
- Traffic management.
- Metrics.
Such integrations must remain optional.
9.3 Standard Kubernetes ingress
On plain Kubernetes, Viking CMS must support standard networking.k8s.io/v1 Ingress resources.
Ingress controllers differ in their support for authentication annotations and other extensions.
Viking CMS must not assume that arbitrary authentication annotations work across controllers.
A portable authentication implementation may require an additional authentication proxy or adapter.
Unsupported configurations must produce clear status conditions.
9.4 TLS
The operator should support existing TLS Secrets and optional integration with certificate automation.
Certificate provisioning must not be hardwired to one implementation.
TLS configuration should be separately configurable for public and CMS hostnames.
10. Observability
10.1 Principles
All components must provide structured logs and operational metrics.
The system must integrate with common Kubernetes observability stacks.
Preferred compatibility targets:
- Prometheus.
- VictoriaMetrics.
- Loki.
- Grafana.
No observability backend is mandatory for basic operation.
10.2 Metrics
Prometheus-compatible metrics should include:
http_requests_total
http_request_duration_seconds
http_response_size_bytes
http_requests_in_flight
Additional metrics:
- Publication duration.
- Publication success/failure.
- Current release age.
- Operator reconciliation duration.
- Reconciliation failures.
- CMS request duration.
- Storage-related failures.
Metric labels must have controlled cardinality.
Do not use arbitrary URLs, unvalidated Host headers or user-controlled values as unrestricted metric labels.
10.3 Logging
Logs should be emitted to stdout/stderr.
JSON structured logging is preferred.
Access logs should contain:
- Timestamp.
- Website identifier.
- HTTP method.
- Request path.
- Status code.
- Response size.
- Request duration.
- Client address where safely available.
- User agent, where configured.
Forwarded client-address headers must only be trusted from known proxies.
Sensitive request data must not be logged unnecessarily.
10.4 Website statistics
The initial system should provide metrics and logs suitable for external dashboards.
Future reporting may include:
- Page views.
- Bandwidth.
- Popular pages.
- Referrers.
- Error rates.
- Crawler traffic.
- Visitor estimates.
These features may be provided by Grafana dashboards or an optional analytics component.
Viking CMS should not require a database solely for website statistics.
11. Security Model
11.1 Isolation
Each Website has an independent frontend Deployment and Service.
This provides:
- Independent scaling.
- Independent resource limits.
- Independent rolling updates.
- Separate mounted content.
- Reduced cross-website exposure.
Kubernetes namespace isolation may provide additional boundaries.
11.2 Public serving layer
The serving layer must have:
- No CMS credentials.
- No private content access.
- No database access.
- No Kubernetes API access.
- No shell.
- No writable application filesystem.
- No unnecessary network egress.
The published filesystem contains only public data.
11.3 CMS layer
The CMS is authenticated and isolated from public serving traffic.
The CMS requires write access to the private PVC and the published RWX PVC.
NetworkPolicy should prevent public serving Pods from connecting to CMS services.
The publisher must validate generated output and uploaded content.
11.4 Container security
Required security settings where supported:
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
ServiceAccount token automounting must be disabled for components that do not need Kubernetes API access.
11.5 Supply-chain security
CI should include:
- Dependency scanning.
- Static analysis.
- Container scanning.
- SBOM generation.
- Release integrity verification.
- Secret scanning.
- Image provenance where practical.
Runtime images must not contain build tools.
12. Installation and Distribution
12.1 Preferred installation
TAK-OS Orbit Deploy is the standard and recommended installation mechanism.
The project should provide a ready-to-use Orbit Deploy configuration.
12.2 Portable installation
Viking CMS must also support installation through standard Kubernetes manifests.
Helm must not be required.
The installation process should be documented for:
- TAK-OS.
- Kubernetes with Orbit Deploy.
- Kubernetes with TAK-OS Ingress.
- Plain Kubernetes.
12.3 Installation components
The core installation includes:
- Website CRD.
- Operator Deployment.
- Operator ServiceAccount.
- Operator RBAC.
- Required configuration.
Website-specific workloads are created by the operator when Website resources are applied.
12.4 Upgrades
Upgrades must preserve:
- Website specifications.
- Published content.
- Private CMS content.
- Existing releases subject to retention.
- Credentials and Secret references.
CRD schema evolution must be planned carefully.
Breaking changes require documented migration procedures.
13. Testing Strategy
13.1 Unit tests
Cover:
- Hostname validation.
- Alias expansion.
- CRD validation.
- Resource generation.
- File path sanitisation.
- HTTP conditional requests.
- Range handling.
- Publishing metadata.
- Release retention.
- Rollback logic.
13.2 Integration tests
Cover:
- Website creation.
- Resource reconciliation.
- PVC provisioning.
- Ingress generation.
- CMS authentication configuration.
- Website modification.
- Website deletion.
- Publication.
- Rollback.
- Failed publication.
- Operator restart recovery.
13.3 End-to-end tests
Use a disposable Kubernetes environment where practical.
Verify:
- Install Viking CMS.
- Create a Website resource.
- Wait for resources to become ready.
- Access the CMS.
- Create a page.
- Publish.
- Retrieve the page through ingress.
- Modify and republish.
- Confirm the old release remains available until activation.
- Confirm the new release becomes active.
- Verify data retention during deletion.
13.4 Performance tests
Benchmark the serving layer independently from ingress.
Then benchmark the complete request path.
Compare:
- Go
net/http. - Go
fasthttp. - Apache HTTPD baseline.
- Optional Rust implementation.
Performance results must distinguish:
- Cached versus uncached reads.
- Local filesystem versus RWX filesystem.
- Ingress versus direct Pod access.
- Single replica versus multiple replicas.
- CPU-bound versus storage-bound workloads.
No unsupported performance claims should be made.
13.5 Security tests
Include:
- Directory traversal attempts.
- Symlink escape attempts.
- Malformed HTTP requests.
- Oversized request headers.
- Unsupported HTTP methods.
- Authentication bypass attempts.
- Unauthorized CMS access.
- File upload validation.
- Publication race conditions.
- Cross-website isolation.
- Kubernetes RBAC validation.
- Container privilege validation.
14. Development Workstreams
The project should support parallel development by multiple agents.
Each workstream must have a defined interface and ownership boundary.
Workstream A ? Kubernetes Operator
Primary responsibility: Website CRD and resource reconciliation.
Deliverables:
- CRD schema.
- Go API types.
- Validation.
- Operator controllers.
- Deployment generation.
- Service generation.
- Ingress generation.
- PVC generation.
- Status conditions.
- RBAC.
- Unit and integration tests.
Dependencies:
- Agreed Website API.
- Container image names.
- Storage mount conventions.
Workstream B ? Static Webserver
Primary responsibility: High-performance static file delivery.
Deliverables:
- Go HTTP server.
- Filesystem path safety.
- HTTP cache semantics.
- Health endpoints.
- Metrics.
- Structured logging.
scratchimage.- Benchmarks.
- Security tests.
Dependencies:
- Published directory contract.
This workstream should be independently executable without Kubernetes.
Workstream C ? CMS Backend
Primary responsibility: Editable project management.
Deliverables:
- CMS backend API.
- Filesystem project storage.
- Page management.
- Media management.
- Draft handling.
- Integration interface for visual editor.
- Authentication/session boundary.
- Container image.
- Tests.
Dependencies:
- Editor integration contract.
- Publishing interface.
Workstream D ? Visual Editor
Primary responsibility: Nontechnical website editing experience.
Deliverables:
- GrapesJS proof of concept.
- Alternative editor evaluation if needed.
- Page editing interface.
- Component library.
- Responsive preview.
- Project serialization.
- Backend API integration.
- Build pipeline.
Dependencies:
- CMS API contract.
GrapesJS adoption remains provisional until evaluated.
Workstream E ? Publisher
Primary responsibility: Static rendering and publication.
Deliverables:
- Rendering interface.
- Release directory management.
- Atomic activation.
- Publication validation.
- Retention management.
- Rollback.
- Failure recovery.
- Tests.
Dependencies:
- CMS project format.
- Published directory contract.
Workstream F ? CI/CD and Deployment
Primary responsibility: Build, testing and release automation.
Deliverables:
- GitLab CI pipeline.
- Multi-architecture builds.
- Security scanning.
- Container publishing.
- Kubernetes installation manifests.
- Orbit Deploy examples.
- Release automation.
- Documentation.
Dependencies:
- Build conventions from each component.
15. Development Coordination
15.1 Shared contracts
Before parallel implementation begins, agree on:
- Website CRD schema.
- Resource naming conventions.
- Container image names.
- Container ports.
- Health endpoint paths.
- Metrics endpoint paths.
- CMS API conventions.
- Editable project storage format.
- Publisher interface.
- Published filesystem layout.
- Release metadata format.
- Authentication adapter interface.
These contracts should be documented under docs/architecture/.
15.2 Agent working rules
Each agent should:
- Work within its assigned component.
- Avoid modifying unrelated components.
- Preserve agreed interfaces.
- Add tests for new functionality.
- Keep dependencies minimal.
- Follow the mandatory container rules.
- Document significant design decisions.
- Avoid introducing Helm.
- Avoid introducing mandatory TAK-OS dependencies.
- Use ordinary Kubernetes APIs where possible.
Shared API changes must be coordinated before implementation.
15.3 Integration
Integration should occur continuously rather than at the end of development.
Each component must have a standalone test path.
The operator, webserver, CMS and publisher should be independently testable.
16. Implementation Milestones
Milestone 1 ? Repository and Architecture
Goal: Establish the monorepo and shared contracts.
Tasks:
- Create repository.
- Confirm license.
- Establish directory structure.
- Define Go workspace/modules.
- Create baseline GitLab CI.
- Define Website CRD schema.
- Define resource naming conventions.
- Define publishing filesystem contract.
- Define CMS/publisher interface.
- Create initial documentation.
Acceptance criteria: All workstreams can begin without inventing incompatible interfaces.
Milestone 2 ? Static Serving Prototype
Goal: Serve a website from a read-only mounted directory.
Tasks:
- Implement minimal Go server.
- Implement required HTTP semantics.
- Implement path safety.
- Implement health endpoints.
- Implement metrics.
- Build
scratchimage. - Deploy manually to Kubernetes.
- Benchmark against Apache.
- Compare
net/httpandfasthttp.
Acceptance criteria: A statically compiled, non-root Go container reliably serves static files from a read-only PVC.
Milestone 3 ? Website Operator
Goal: Provision the frontend automatically.
Tasks:
- Implement Website reconciliation.
- Create published PVC.
- Create frontend Deployment.
- Create Service.
- Create Ingress.
- Implement status conditions.
- Implement deletion and retention handling.
- Add integration tests.
Acceptance criteria: Applying a Website resource creates a working public static website without manual resource creation.
Milestone 4 ? CMS Infrastructure
Goal: Provision a private, authenticated CMS environment.
Tasks:
- Create private PVC.
- Create CMS Deployment.
- Create CMS Service.
- Create authenticated CMS Ingress.
- Implement authentication configuration validation.
- Add CMS readiness reporting.
- Verify network isolation.
Acceptance criteria: The operator provisions a CMS that is accessible only through configured authentication.
Milestone 5 ? Visual Editor Proof of Concept
Goal: Validate the selected editing technology.
Tasks:
- Evaluate open-source GrapesJS.
- Create a multipage site visually.
- Save editable project state.
- Reload project state.
- Upload and manage images.
- Export HTML/CSS/assets.
- Validate responsive behaviour.
- Record limitations and licensing findings.
Acceptance criteria: A nontechnical user can create and edit a simple multipage website without writing code.
Decision gate: Confirm GrapesJS or select an alternative.
Milestone 6 ? Publishing
Goal: Publish complete websites without interrupting serving.
Tasks:
- Implement release generation.
- Implement validation.
- Implement atomic activation.
- Implement release metadata.
- Implement retention.
- Implement rollback.
- Test interrupted publishing.
- Test concurrent publishing.
- Test publication under load.
Acceptance criteria: A website can be edited, rendered and atomically published while the existing version remains available.
Milestone 7 ? Complete CMS Workflow
Goal: Deliver the end-to-end user experience.
Tasks:
- Integrate editor and backend.
- Implement project storage.
- Implement draft saving.
- Implement media management.
- Implement preview.
- Implement Publish action.
- Implement publication status.
- Implement basic SEO metadata.
- Implement sitemap generation.
- Add user documentation.
Acceptance criteria: A site owner can create, edit and publish a website entirely through the browser.
Milestone 8 ? Portability and Hardening
Goal: Validate operation across supported Kubernetes environments.
Tasks:
- Test with TAK-OS.
- Test with TAK-OS Ingress.
- Test with standard Kubernetes Ingress.
- Test Orbit Deploy installation.
- Test plain-manifest installation.
- Complete security testing.
- Complete performance testing.
- Verify upgrade behaviour.
- Verify data retention.
- Complete documentation.
Acceptance criteria: Core Viking CMS functionality works without mandatory TAK-OS dependencies.
Milestone 9 ? Initial Public Release
Goal: Publish a usable open-source release.
Tasks:
- Finalise versioned CRDs.
- Publish container images.
- Publish installation manifests.
- Publish examples.
- Publish administrator guide.
- Publish site-owner guide.
- Publish performance findings.
- Publish security documentation.
- Create GitLab release.
- Prepare project announcement.
Acceptance criteria: An independent Kubernetes administrator can install Viking CMS and publish a website using the documentation.
17. Open Design Decisions
The following items remain deliberately unresolved.
| Area | Decision required |
|---|---|
| HTTP server | Go net/http, fasthttp, or Rust |
| Visual editor | GrapesJS or alternative |
| CMS project format | Final editable content schema |
| CMS backend | Go preferred, subject to editor needs |
| Authentication | Portable OAuth2/OIDC implementation |
| Ingress integration | Adapter interface and feature detection |
| Storage | Default storage class and filesystem guarantees |
| Publication | Release metadata and safe cleanup strategy |
| Scaling | Fixed replicas initially; HPA metrics later |
| CMS availability | Single replica initially |
| Backup | PVC snapshot and off-cluster backup strategy |
| Licensing | Confirm MIT and dependency compatibility |
| API group | Confirm cms.tlspu.com |
| Website analytics | Grafana dashboards or dedicated UI |
| Multi-user editing | Initial scope and conflict handling |
| Site templates | Bundled starter templates and format |
These decisions must not block independent development where interfaces can be agreed in advance.
18. Future Enhancements
Potential future development includes:
- HPA support per website.
- Request-rate-based autoscaling.
- Local content caching.
- OCI-image-based website publishing.
- Shared theme libraries.
- Visual template marketplace.
- Blog and structured content collections.
- WordPress content migration.
- Contact-form handling.
- Scheduled publication.
- Multi-user collaboration.
- CMS role-based permissions.
- Advanced analytics dashboards.
- Website cloning.
- Website export/import.
- Disaster-recovery tooling.
- Multiple CMS implementations.
- Optional object-storage publication.
- CDN integration.
- Gateway API support.
- Advanced TAK-OS Ingress integration.
These should remain outside the initial MVP unless needed to validate a fundamental architectural assumption.
19. Definition of Done
The initial Viking CMS release is complete when:
- A Kubernetes administrator can install the operator using standard manifests or Orbit Deploy.
- A Website custom resource provisions the required frontend and CMS resources.
- The public frontend starts with two replicas.
- The frontend serves static files from a read-only RWX PVC.
- The CMS stores editable project data on a separate private PVC.
- The CMS is protected by exactly one supported authentication mechanism.
- A nontechnical user can create and edit a simple website visually.
- The CMS can generate a complete static publication.
- Publication switches the live website atomically.
- Failed publications do not replace the active website.
- Release retention defaults to zero historical releases.
- Optional retained releases can be rolled back.
- The public webserver has no access to private CMS data.
- Go binaries are built with
CGO_ENABLED=0and run inscratchcontainers. - Other language runtimes use Alpine-based containers.
- Metrics and structured logs are available.
- Core functionality works on plain Kubernetes.
- Automated tests cover the critical paths.
- Security and performance testing have been completed.
- Installation, operation and user documentation are available.
20. Final Architectural Statement
Viking CMS is a Kubernetes-native, visually managed, statically published website hosting platform.
It combines the accessibility of a visual CMS with the operational advantages of Kubernetes and the performance and security characteristics of a minimal static HTTP server.
Website creation and content management are deliberately isolated from public content delivery.
Each website is independently provisioned, deployed and scaled through a Kubernetes Custom Resource.
The platform uses ordinary Kubernetes APIs, integrates preferentially with TAK-OS Orbit Deploy and TAK-OS Ingress, and remains independently usable on standard Kubernetes.
The primary objective is not to recreate the complexity of conventional web hosting.
It is to eliminate the complexity that is no longer necessary.
Viking CMS
A TLSPU Limited project.
