The charm had no ``hooks/init``, so Carbone started in its default
stateless mode: no Studio, no template management, and ``CARBONE_BIND``
defaulting to ``127.0.0.1`` (unreachable behind the web-proxy).
Add ``hooks/init`` to:
- bind on ``0.0.0.0`` so the ``web-proxy`` can reach the service ;
- serve the Studio and ``/carbone-studio.js`` (``CARBONE_STUDIO``) ;
- enable stateful template management (``CARBONE_TEMPLATE_MANAGEMENT``):
stable 64-bit template IDs, versioning and the ``/template`` endpoints ;
- align the datastore/configstore ownership on the unprivileged
``carbone`` image user, otherwise the container crash-loops with
``EACCES`` on ``/app/config/config.json``.
Persist ``/app/config`` as a ``config-resources`` and set a
``docker-compose.stop_grace_period`` of 200s: Carbone flushes its
template metadata on graceful shutdown, and Docker's 10s default would
SIGKILL it and lose templates on redeploy. Flush metadata every
5 minutes to shrink the hard-crash window.
Bump the image to ``full-5.15.1`` and add a local ``basic-deploy`` test.
Verified locally: ``/status`` 200, ``/carbone-studio.js`` 200,
``GET /templates`` 200, template upload with versioning plus render
from the stable template ID, and metadata survival across a container
recreation.
Outline runs as an unprivileged user in the image (``nodejs`` since
1.10.0, ``root`` up to 1.6.1) while the datastore is provisioned by
``root``. Without realignment the application cannot write its
``uploads``, ``public`` and ``avatars`` buckets, so every attachment
upload fails with "Permission denied writing to ... Check the host
machine file system permissions". This was seen on elabore.coop
after the 1.6.1 to 1.10.0 upgrade.
The ``init`` hook now reads the image's ``Config.User`` and chowns the
datastore to that user, skipping root-based images. It is idempotent
and version-agnostic: the realignment also covers buckets added by
future Outline versions.
The ``pre_deploy`` hook reassigns every object of the outline database
(tables, sequences, views, materialized views, standalone types,
functions, procedures) to the application role before the container
runs its migrations.
Historical provisioning or restores running as the ``postgres``
superuser leave objects owned by ``postgres``, so any later ``ALTER``
on them fails with "must be owner of ..." and puts outline in a
crash-loop at migration time. Seen on elabore.coop when upgrading
1.6.1 -> 1.10.0: migration
``20260714000000-add-mcp-to-search-queries-source.js`` failed on
``enum_search_queries_source``.
Extensions are excluded from the realignment (they are managed by the
``postgres`` charm). The hook is idempotent, silent when there is no
drift, and blocks the deployment (``exit 1``) if any drift remains, so
the problem surfaces at deploy time instead of as a cryptic
crash-loop.