From 75d5504729adf1f7bedc81c6dc9b06494350ca84 Mon Sep 17 00:00:00 2001 From: codex Date: Tue, 25 Aug 2026 17:52:28 +0200 Subject: [PATCH] refactor(modules): load wasm modules from templates --- Dockerfile | 1 - README.md | 2 +- catalog/embed.go | 2 +- catalog/palworld/README.md | 5 +- .../palworld/module}/LICENSE | 0 .../palworld/module}/README.md | 9 +- .../palworld/module}/fixtures/metrics.json | 0 .../palworld/module}/manifest.yaml | 0 .../palworld/module}/module.wasm | Bin .../palworld/module}/src/main.go | 0 .../palworld/module}/src/main_stub.go | 0 catalog/palworld/template.yaml | 5 +- cmd/dogama/main.go | 3 +- docs/PROJECT-STATE.md | 2 +- docs/architecture/system-architecture.md | 3 +- docs/architecture/wasm-modules.md | 37 ++--- docs/contributing/ai-codex-guide.md | 14 +- docs/contributing/development.md | 23 +++- internal/catalog/local_test.go | 94 +++++++++++++ internal/catalog/template.go | 127 +++++++++++++++++- internal/catalog/template_test.go | 38 +++++- internal/instance/module_service.go | 24 ++-- internal/module/runtime_test.go | 4 +- internal/persistence/sqlite/catalog.go | 21 ++- internal/persistence/sqlite/catalog_test.go | 3 + internal/persistence/sqlite/schema.sql | 1 + internal/persistence/sqlite/store.go | 38 +++++- internal/web/server_test.go | 2 +- specs/template.schema.json | 8 ++ tools/validate_spec.py | 10 +- 30 files changed, 409 insertions(+), 67 deletions(-) rename {modules/palworld-rest => catalog/palworld/module}/LICENSE (100%) rename {modules/palworld-rest => catalog/palworld/module}/README.md (77%) rename {modules/palworld-rest => catalog/palworld/module}/fixtures/metrics.json (100%) rename {modules/palworld-rest => catalog/palworld/module}/manifest.yaml (100%) rename {modules/palworld-rest => catalog/palworld/module}/module.wasm (100%) rename {modules/palworld-rest => catalog/palworld/module}/src/main.go (100%) rename {modules/palworld-rest => catalog/palworld/module}/src/main_stub.go (100%) diff --git a/Dockerfile b/Dockerfile index 66d800d..052ae3d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,6 @@ RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache FROM gcr.io/distroless/static-debian12:latest@sha256:a9fcaedd4c9b59e12dd65d954f0b5044f19b0647a8a3712e77205df9e7b102cd AS dogama WORKDIR /var/lib/dogama COPY --from=build /out/dogama /usr/local/bin/dogama -COPY --from=build /src/modules /usr/share/dogama/modules EXPOSE 8080 ENTRYPOINT ["/usr/local/bin/dogama"] diff --git a/README.md b/README.md index babce4a..9103e1d 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ Report reproducible problems in the [Gitea issue tracker](https://git.zaynet.fr/ ## License -No repository-wide license file is currently present, so no general redistribution license is asserted here. The Palworld reference module has its own [license](modules/palworld-rest/LICENSE). A project-wide license must be added by the owner before public distribution. +No repository-wide license file is currently present, so no general redistribution license is asserted here. The Palworld reference module has its own [license](catalog/palworld/module/LICENSE). A project-wide license must be added by the owner before public distribution. ### Web access and languages diff --git a/catalog/embed.go b/catalog/embed.go index fc452b2..45669aa 100644 --- a/catalog/embed.go +++ b/catalog/embed.go @@ -5,5 +5,5 @@ import "embed" // Files contains built-in templates and their packaged assets. // -//go:embed */template.yaml */assets/* +//go:embed */template.yaml */assets/* */module/* */module/*/* var Files embed.FS diff --git a/catalog/palworld/README.md b/catalog/palworld/README.md index 01ded6c..a9b13fb 100644 --- a/catalog/palworld/README.md +++ b/catalog/palworld/README.md @@ -17,4 +17,7 @@ The upstream API may change. A template snapshot version remains independent fro - The template ships separate local logo and horizontal artwork assets. The source URL and attribution remain in the template so the cached raster can be audited without loading third-party content in the UI. - The schema's storage sizes are conservative product defaults because upstream specifies SSD performance but not a fixed disk-size requirement. - Local hosted-world migration may require player identity conversion. The generic V1 importer detects structure and warns; it does not silently convert identities. -- The checked-in module manifest is a source example. It becomes installable only after `module.wasm` is built and its real SHA-256 replaces the all-zero placeholder. +- `module/` contains the complete Palworld REST adapter: its manifest, source, + fixture, license and compiled `module.wasm`. It is declared by + `template.yaml` and is loaded from the template snapshot, not a global module + registry. Rebuild it with the command in `module/README.md`. diff --git a/modules/palworld-rest/LICENSE b/catalog/palworld/module/LICENSE similarity index 100% rename from modules/palworld-rest/LICENSE rename to catalog/palworld/module/LICENSE diff --git a/modules/palworld-rest/README.md b/catalog/palworld/module/README.md similarity index 77% rename from modules/palworld-rest/README.md rename to catalog/palworld/module/README.md index b14dd0e..1b179e6 100644 --- a/modules/palworld-rest/README.md +++ b/catalog/palworld/module/README.md @@ -31,8 +31,11 @@ From the repository root, using Go 1.25 or newer: ```sh CGO_ENABLED=0 GOOS=wasip1 GOARCH=wasm go build \ -trimpath -buildmode=c-shared \ - -o modules/palworld-rest/module.wasm ./modules/palworld-rest/src -sha256sum modules/palworld-rest/module.wasm + -o catalog/palworld/module/module.wasm ./catalog/palworld/module/src +sha256sum catalog/palworld/module/module.wasm ``` -The checksum must exactly match `manifest.yaml`. Repository tests instantiate the real artifact under wazero and exercise it against a bounded fake Palworld transport. +The checksum must exactly match `manifest.yaml`. The Palworld template declares +`module/manifest.yaml`; DoGaMa discovers the artifact from that template-local +bundle without a game-specific registry. Repository tests instantiate the real +artifact under wazero and exercise it against a bounded fake Palworld transport. diff --git a/modules/palworld-rest/fixtures/metrics.json b/catalog/palworld/module/fixtures/metrics.json similarity index 100% rename from modules/palworld-rest/fixtures/metrics.json rename to catalog/palworld/module/fixtures/metrics.json diff --git a/modules/palworld-rest/manifest.yaml b/catalog/palworld/module/manifest.yaml similarity index 100% rename from modules/palworld-rest/manifest.yaml rename to catalog/palworld/module/manifest.yaml diff --git a/modules/palworld-rest/module.wasm b/catalog/palworld/module/module.wasm similarity index 100% rename from modules/palworld-rest/module.wasm rename to catalog/palworld/module/module.wasm diff --git a/modules/palworld-rest/src/main.go b/catalog/palworld/module/src/main.go similarity index 100% rename from modules/palworld-rest/src/main.go rename to catalog/palworld/module/src/main.go diff --git a/modules/palworld-rest/src/main_stub.go b/catalog/palworld/module/src/main_stub.go similarity index 100% rename from modules/palworld-rest/src/main_stub.go rename to catalog/palworld/module/src/main_stub.go diff --git a/catalog/palworld/template.yaml b/catalog/palworld/template.yaml index c13a8e3..83d43a8 100644 --- a/catalog/palworld/template.yaml +++ b/catalog/palworld/template.yaml @@ -1,6 +1,6 @@ schema_version: 1 id: palworld-official -version: 1.1.2 +version: 1.1.3 source: type: official @@ -186,6 +186,9 @@ integration: port_id: rest_api required: false +module: + path: module/manifest.yaml + backup: strategy: online_save source_mounts: diff --git a/cmd/dogama/main.go b/cmd/dogama/main.go index a038f14..70787a0 100644 --- a/cmd/dogama/main.go +++ b/cmd/dogama/main.go @@ -46,7 +46,6 @@ func run(logger *slog.Logger) error { databasePath := environment("DOGAMA_DATABASE_PATH", "dogama.db") serversRoot := environment("DOGAMA_SERVERS_ROOT", "/srv/game-servers") templatesRoot := environment("DOGAMA_TEMPLATES_ROOT", "/var/lib/dogama/templates") - modulesRoot := environment("DOGAMA_MODULES_ROOT", "/usr/share/dogama/modules") ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() @@ -119,7 +118,7 @@ func run(logger *slog.Logger) error { } handler, err = web.NewHandlerCompleteWithCatalogDeploymentAndRuntime(auth.New(db), repository, lifecycle, backupService, importService, auditService, notificationService, func(ctx context.Context) (catalog.ScanResult, error) { return synchronizeCatalog(ctx, repository, templatesRoot) - }, serversRoot, instance.NewModuleService(repository, repository, modulesRoot), logger) + }, serversRoot, instance.NewModuleService(repository, repository), logger) if err != nil { return err } diff --git a/docs/PROJECT-STATE.md b/docs/PROJECT-STATE.md index f0e3796..e71026b 100644 --- a/docs/PROJECT-STATE.md +++ b/docs/PROJECT-STATE.md @@ -58,7 +58,7 @@ Read this compact operational baseline before starting a milestone. Open detaile - Label values support only the explicit allowlist in `internal/instance/container_config.go`; unknown variables are errors, not arbitrary templates. - `{{game.icon_url}}` is the public icon for the game. `{{instance.slug}}` remains supported. - Template game artwork contains separate required local `logo` and horizontal `image` assets; template validation rejects missing files. Deployment previews expose distinct logo and artwork URLs while retaining `icon_url` as a compatible logo alias. Palworld template `1.1.0` is the first snapshot with this contract. -- Embedded catalog validation is collection-based: every discovered template is schema- and cross-field-validated, including referenced assets, declared integration modules and ports/configuration. Template asset contents are not SHA-256-pinned, allowing administrator-maintained local assets while preserving required-path validation. A template `version` identifies a DoGaMa snapshot, whereas `container.tag` selects the game-server image; the official Palworld template follows Pocketpair's `latest` tag for new deployments and ordinary pulls. +- Embedded catalog validation is collection-based: every discovered template is schema- and cross-field-validated, including referenced assets, declared template-local integration modules and ports/configuration. A module bundle lives at `