3 Commits
Author SHA1 Message Date
Juan DenisandClaude Fable 5 586f556b93 Reference template icons through the serverkit.ai proxy
Follow-up to 025fa0be: keep serverkit.ai as the stable icon host rather
than jsDelivr. The registry repo remains the canonical home of the logo
files; serverkit.ai proxies /imgs/template-icons/ from its raw tree, so
these URLs now resolve for all 210 logos instead of only the legacy
subset the website bundles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 21:28:14 -04:00
Juan DenisandClaude Fable 5 025fa0be7d Point bundled template icons at registry-hosted logos
The offline template bundle carried the same generic stroke-icon data
URIs the registry had, and even the templates with real icons pointed at
serverkit.ai, coupling icon availability to website deploys. Icons now
live in the serverkit-templates repo itself, so reference them through
its jsDelivr mirror. Also fixes pgvector/timescaledb extensions, which
referenced a postgresql.svg that never existed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 21:18:47 -04:00
Juan DenisandClaude Opus 5 679082bf16 feat(databases): engine extensions, without the silent failure
The third deferred item. An extension installs INTO a running engine rather than
getting its own container, so it lives in its own namespace:
`backend/templates/extensions/<id>.yaml` with a top-level `extension:` block,
discovered by the same operator-wins two-directory scan the app templates use.
Dropping a YAML in makes it available with no code change, and a test pins that.

Not nested inside the engine template, because pgvector applies to any
PostgreSQL-speaking engine rather than to `postgresql.yaml` — nesting would force
duplication per host. Not a third `kind:` in the app catalog either: an
extension has no compose, port or volume and produces no Application, so every
consumer of `list_all_templates` would have had to learn to skip it.

The hard part is not the SQL. `CREATE EXTENSION vector` is valid and fails on
stock `postgres:16` with "could not open extension control file", because that
image does not ship the library. Two layers, and the statement is never sent
speculatively: an extension declares which images provide it, so a listing marks
an instance unsupported BEFORE anything is clicked; then at install
`pg_available_extensions` is queried first and a negative answer is a 409 with a
remedy, not an attempt. The live probe overrules the image name in both
directions — a private `registry.internal/ops/pg-with-vector` is allowed, and a
`pgvector/pgvector` tag that does not actually carry the library is still
refused.

ServerKit does not recreate a container behind the operator's back. The remedy
instead names an engine template that IS capable, found by matching the
extension's images against each engine template's compose — hence
`postgresql-pgvector.yaml`. TimescaleDB deliberately ships no bundled host and
says so rather than implying one exists.

Compatibility also needed correcting mid-build: matching on wire protocol alone
claimed CockroachDB could host pgvector. It answers on pgwire and cannot load a
single extension. Engines opt in with `engine.extensions: true`, which excludes
it without any Python naming it.

Two fixes fell out. `DatabaseService` had no psql twin of `docker_mysql_execute`
— `managed_db_user_service` carried a private copy with a comment saying as
much; there is now one implementation and it delegates. And that twin sets
`ON_ERROR_STOP=1`, without which psql reports SUCCESS for a statement the server
rejected.

57 tests, covering the zero-code-change property, compatibility, the image
problem in both probe directions, admin gating, and refusals that must not
execute anything.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 23:44:34 -04:00