Skip to content
Dekglas
Marolum documentation

Known limitations

What the current build does not do, in terms you can check against the running product.

Known limitations

Every entry describes what the current build does not do. Nothing here is a roadmap; a limitation leaves this page when the behavior changes, not when a change is planned. Read it before deciding whether Marolum belongs in your environment.

Deployment and storage

SQLite is one replica, and PostgreSQL is yours to run

SQLite single-replica is Community's supported topology and it is complete. Beginning a SQLite-to-PostgreSQL adoption is Business; multiple serving PostgreSQL replicas are Enterprise. PostgreSQL is customer-operated: Marolum does not supply, patch, replicate, or back up the server.

The single-replica rule is enforced. The process holds an exclusive lock on marolum.db.owner; a second replica on the same file or shared volume fails to start with ownership-conflict.

Consequences on SQLite:

  • No horizontal scaling and no rolling deployment. An upgrade is a stop, then a start, and the restart is downtime.
  • WAL mode requires host-local locking. EFS, NFS, SMB, Azure Files, and shared ReadWriteMany volumes are outside the supported envelope even when the platform advertises POSIX locking. So are emptyDir, task scratch, the container writable layer, and any ECS service-managed EBS volume tied to a task.
  • Login throttling is process-local and resets on restart.

On PostgreSQL, every replica must share the same release, database, and configuration. Lapse keeps one primary serving and the others alive but not ready. Application transactions serialize installation-wide with a five-second contention bound; more replicas do not mean proportional write throughput. Change notifications have a fifteen-second poll fallback.

Upgrades are forward-only; a downgrade is a restore

Migrations run at startup, in order, transactionally, forward only. Each release declares the oldest schema revision it upgrades from and refuses older databases before touching them; a migration classified major, destructive, or long-running needs an explicit apply and a verified recovery point. A failed migration rolls back and leaves nothing partial.

An older image started against a database a newer one migrated refuses to start, naming both revisions. Pointing an older image at a migrated volume is never a rollback path; rolling back means restoring the pre-upgrade backup. A release that applies no migration, such as 0.21.1 after 0.20.2, leaves nothing for the older image to refuse: it starts, and the version stamp then names it. Restoring the backup is still the rollback.

PostgreSQL needs a licensing directory through 0.21.1

In releases up to and including 0.21.1, a PostgreSQL installation must set MAROLUM_CONNECTED_LICENSE_DIR to an existing directory owned by UID 1000 with mode 0700. An empty one is enough: the installation ID lives in the shared database, and the directory only seeds it on the first start. Without the setting, startup logs Marolum failed to start. and the process does not exit, so the container stays up and never becomes ready.

0.22.0-rc.1 removes this unused-directory requirement and exits after a refused start. The earlier images retain the workaround above; upgrading does not change their bytes.

Backups are cold, and Marolum cannot verify one exists

The supported SQLite backup is: stop, copy marolum.db and its licensing directory, start. A copy taken while the process runs is not valid. There is no scheduled backup, no online backup command, and no restore endpoint. Every destructive administration flow reports backupVerification: unknown and requires the administrator to attest that they took and verified a backup themselves.

Deleting evidence does not free disk

Retention expiry, retroactive shortening, purge, and forget free bytes for reuse inside the SQLite file. The file never shrinks and the volume never recovers space until an offline VACUUM runs against a stopped replica. The console's reclaimed-storage figures describe internal reuse and are a labelled lower bound. PostgreSQL needs its operator's own vacuum maintenance.

Retention is keyed to a backend's identity

The practical consequences for every backend change are in Changing your backends safely.

Marolum's retention governs Marolum's copy. It never deletes anything from your bucket. The fence that stops expired history being re-admitted is keyed on the configured backend id. Removing and re-adding the same id does not create a new identity or reset its fences. A new id starts a separate history: retained source records can be read again as new evidence with a fresh retention clock. Never reuse an id for different state or change it to bypass expiry. Explicit forget is a separate deletion act.

Coverage

History pruned before Marolum observed it is gone

Marolum's record begins when it first successfully reads a stack. Anything the backend had already pruned was never observed, and Marolum does not reconstruct, extrapolate, or back-fill it. The console says so with History not observed, Before coverage began, Retention-limited interval, and Evidence pruned by retention rather than hiding it.

A restore rewinds Marolum's record to the backup point. Backends converge forward from live state, but history from before the backup is whatever the backup held.

No attribution

The admitted field set from an update record has no actor, user, email, CI job, commit, or branch. No row will ever say who ran an operation, and no filter can search by person. Runner evidence carries the token's authority kind and, for administrators, its name; that is credential provenance, not human identity.

Transcript capture is bounded

Capture is off by default and needs a nonzero retention window, a token with capture permission, an explicit runner request, the external key, valid redaction configuration, and storage headroom. Output before permission arrives, queue overflow, interrupted delivery, and limit exhaustion leave explicit gaps. Collection admits at most 512 chunks, 8 MiB, and four hours per operation; retained text is at most 2 MiB as head and tail. Redaction cannot recognize every secret. Search, terminal emulation, and clickable content are not provided.

Runner integration

  • Backend/runner joins require the exact retained Marolum marker, and stay two rows. A matching update records that both sources describe one run, but Operations still lists the backend row and the runner row separately. The join is visible only when the runner never finalized: its row then shows the backend's outcome, marked adopted from correlated record. Previews remain runner-only; missing markers or backend history pruned before observation leave no join. Similar timestamps or names do not establish a join.
  • Comparisons require explicit correlation and reported counts. Marolum never infers a preview-to-apply match from timestamps, names, or similar counts.
  • policy-denied requires policy evidence. An exit code alone cannot establish it.
  • Counts require a fresh event log. Reused, malformed, oversized, or combined preview/apply logs leave counts unavailable.
  • No local spool. Undelivered evidence is lost and says so.
  • Actor labels, branch names and arbitrary build URLs are never accepted. Commit revisions and GitHub run identity use the closed CI context in announcement schemas 4 and 5.

The console

  • Export is one stack, one window, at a time. No installation-wide export, no CSV, no scheduled or API-triggered export; the format is the versioned JSON document, paged at 100 rows.
  • There is no source-kind filter. You cannot narrow the timeline to backend-only or runner-only evidence; read the source labels on individual operations.
  • unknown is unreachable. It exists in the vocabulary but no source produces it, so the filter omits it.
  • Resource-to-operation association is temporal, not causal. The Operations near this change panel lists overlaps in time and labels them temporal.
  • English only, tested under Chromium only. Timestamps use a hardcoded en-US calendar with a UTC/local toggle. Firefox and WebKit are not exercised by any automated test.
  • No self-service account management. No signup, invitation, self-service password change, or session list. Lost bootstrap-administrator recovery is a file-based rotation with a restart.
  • Stack grants are not tenant isolation. They narrow retained-history reads; live observation pages remain installation-wide. Fixed administrators are never scoped.

Supported state formats

  • Marolum reads Pulumi deployment checkpoints at envelope versions 0 through 4, with version 4 accepted only when its declared features are on Marolum's pinned allowlist. Unknown versions and features fail closed as distinct, visible conditions.
  • The verified Pulumi CLI window is 3.245.0 through 3.258.0. Each version writes a corpus of real state that a conformance check reads on every build. State written by a CLI outside that window is unverified.
  • Pulumi has deprecated the legacy DIY layout and intends to remove it. Marolum reads legacy state directly from S3 and is unaffected by the CLI change, but treat a legacy backend as something to migrate.
  • Other-engine backends are read at state version 4 only, from keys shaped env:/<workspace>/<project>/terraform.tfstate or <project>/terraform.tfstate beneath the configured prefix. Other versions, layouts, and multi-segment keys are unobserved.

Engine support bounds. State observation is verified against Terraform 1.15.9 and OpenTofu 1.12.6; other builds are unverified. Terraform and OpenTofu can write native S3 lock files when locking is enabled, but Marolum does not currently observe them. Backend-native update history remains unavailable for those engines, so lock and update-history observation are Pulumi-only and these backends answer engine-unsupported for both rather than showing empty results.

Business features

  • Whole-state capture is off by default and requires explicit backend opt-in under a Business or Enterprise license that includes it, a separately backed-up key, and storage headroom. Copies already retained stay recoverable after a downgrade. There is a 16 MiB per-copy ceiling. Retained state can include secrets and is available only through separate recovery grants. Lost keys cannot be reconstructed.

Enterprise features

  • External audit delivery is a core-event feed for the named administration audit surfaces, not an export of every action. One fixed HTTPS receiver; buffering is limited to 10,000 references for seven days with visible overflow counters; retries can duplicate accepted events after an acknowledgement loss or restore. No exactly-once, ordering, completeness, or delivery SLA claim is made.

Property evidence and rollout

Property capabilities are included in Marolum's published images and tested locally. Human journey acceptance, compatible application deployment, issuer activation and supported status are separate gates. Existing licences may lack the property claims: enabling the issuer's rollout setting does not rewrite an existing grant or signed document. New eligible grants can include the claims only after compatible application deployment. Public edition cards retain their accepted capabilities.

Observed property history and runner assertions have separate opt-ins and lifetimes. History captures changed-resource transitions after opt-in; it does not backfill uncaptured versions. Runner property evidence is limited to Pulumi and at most 100 resource/action rows, 512 KiB per operation and 64 KiB per side. Omitted coverage, unknown and redacted values remain explicit. Only an authorized explicit correlation joins planned and actual evidence.

Property redaction cannot recognize every unmarked sensitive value. Immutable runner assertions, preservation holds and backups can retain original bytes after a later masking rule hides them from disclosure. Retained property reads and authorized exports survive licence lapse; granting a licence does not grant a user's independent read/export permissions or enable capture.