feat(catalog): add AppWeaver managed app - #397
Conversation
|
Hi, thanks for the PR, can you explain a bit about the app, what is it, who is it for? It looks to me like a web-based agent harness. |
|
Thanks. It is partly a web-based agent harness, but the intended scope is a self-hosted app hub around that agent backend. AppWeaver is for people who want a private workspace containing focused AI-assisted tools without building and operating separate services for each workflow. OpenCode supplies the agent execution layer; AppWeaver adds the plugin/app runtime, controlled tool registration, commands, web UI, setup, scheduler, persistent data, and Nostr DM access. Current apps include todos, bookmarks, file management, scheduled jobs, publishing tools, and Nostr monitoring. The LNVPS managed version is aimed at users who want this workspace without managing a VPS, container runtime, TLS, or backups. They open the HTTPS setup page, select credentials/providers, and install the apps they need. Their configuration, credentials, databases, installed apps, and app data stay in the persistent managed volume. This catalog image is deliberately the reduced server profile: no Chromium/browser automation, Cursor Agent, desktop, or VNC. |
v0l
left a comment
There was a problem hiding this comment.
Requesting changes on the image build, not on the compose document.
As published, nothing about what a deployment runs is fixed by this entry. Dockerfile.alpine builds FROM alpine:edge and installs Bun, OpenCode and ngit by piping three unpinned installers from three third-party domains into bash, with no checksums. :alpine is a moving tag. Then scripts/docker-entrypoint-alpine.sh clones APPWEAVER_GIT_REF=main into the workspace on first boot and runs bun install --frozen-lockfile on every start.
The consequence is that merging this would let the code running inside customer deployments change at any time, retroactively, with no change to this repository and no review on our side. Anyone who takes the ghcr namespace, the GitHub account, or any of bun.sh / opencode.ai / ngit.dev inherits the same reach. Every other entry in catalog/ pins a version (holgerhatgarkeinenode/haven-docker:v1.2.2, mariadb:11); this one pins nothing at any layer. That is not a risk we can carry for a paid, one-click deployment.
What we need before this can be considered:
- Compile the app into the image. The checkout and its resolved dependencies should be baked in at build time, so a deploy performs no
git cloneand nobun install. The workspace volume should hold customer state only (.env,.data/, databases, installed apps, credentials), not the application itself. Today a deployment cannot start without GitHub and the npm registry both being reachable, and a lockfile that drifts onmainbreaks every restart of every existing deployment at once. - Pin the base and the toolchain. A specific Alpine release rather than
edge, and Bun / OpenCode / ngit installed at pinned versions with verified checksums rather thancurl | bashof a latest installer. - Publish an immutable version tag, for example
ghcr.io/getappweaver/core:v12.1.2-alpine, so the catalog entry can reference an exact build. The repository already carries 460 release tags, so the version to bake in is not in question. We will pin by digest in the entry.
Measured against the published digest sha256:e36f3d29fc3cac7a7bc1f9ab5708e67ad6c52e800aaa751aade516cafe70e3fe, run under the operator's hardening (read only rootfs, --cap-drop ALL, uid 1000, 1 CPU / 2Gi): first boot pulls roughly 120 MB, takes about 60 seconds to listen, and leaves a 450 MB workspace of which 316 MB is node_modules. Baking it in removes all of that from the deploy path.
Point 1 also fixes an upgrade problem that exists today regardless of the security question. The clone is guarded by if [ ! -f package.json ], so after first boot the checkout is never updated again. Pulling a new image would not upgrade the app, and two customers ordering a week apart would be on different code with no way for us to tell which.
Separately, and also blocking: the setup UI is unusable in a managed deployment. createSetupSecret() generates a fresh random secret on every boot and prints it only to the container log, and every /api/setup/* route requires it. Our customers have no access to container logs, and generated secrets are never returned to them, so there is no way to reach /setup. I confirmed the secret changes across a restart. Since SETUP_SECRET is already read from the environment, the fix on our side is a config: field the customer supplies at order time, which I can add once the image question above is resolved. Worth being aware that this puts an admin interface for an agent with arbitrary code execution behind one static string on a public URL, so it will need a strong required format.
Happy to re-test as soon as there is a tagged, self-contained image.
|
Thanks for the detailed review. One architectural assumption needs clarification: AppWeaver is intentionally distributed as a source workspace rather than as an immutable compiled application. AppWeaver Core is a Bun/TypeScript Git checkout. That checkout is the installation. I intentionally allow the owner to inspect and modify the Core source, including while developing a new AppWeaver app. Each workspace is an independent AppWeaver instance. Every workspace can have its own Core version, installed apps, configuration, databases, app data, generated bindings, and OpenCode sessions. Updating one workspace does not update another. Apps use the same source-based model. Each installed app is a Git repository under Core updates are explicit owner actions, not automatic background mutations. The current updater performs The Alpine image provides the native runtime required by a source workspace: Bun, OpenCode, ngit, Piper, Python/ONNX dependencies, Git, and SSH tooling. I agree that Alpine, these tools, release artifacts, and checksums should be pinned, and that the catalog should reference a versioned image digest. I can also make initial provisioning deterministic without making the resulting workspace immutable. A versioned image can contain a prepared checkout at a reviewed AppWeaver commit with its frozen dependencies already installed. On first start, that checkout is copied into the empty persistent workspace. Deployment therefore needs neither GitHub nor npm access. The checkout remains a normal writable Git repository tracking Setup access has already been addressed in AppWeaver Core commit The matching catalog change is now in this PR as commit The remaining policy question is whether LNVPS accepts a deterministic initial deployment whose authenticated owner can later update or modify the source inside their own persistent workspace. This mutability is an intentional AppWeaver capability. An edition that executes Core only from an immutable image layer would prevent source inspection, modification, Git updates, and workspace-local app development, so it would not represent the current AppWeaver model. |
What is AppWeaver?
AppWeaver is a self-hosted app hub for people who want AI-assisted tools and workflows in a private workspace they control. OpenCode provides the agent backend, while AppWeaver supplies the app/plugin runtime, tool and command registration, web UI, setup flow, scheduling, persistence, and Nostr DM interface. Available apps cover workflows such as todos, bookmarks, files, scheduled jobs, publishing, and Nostr monitoring.
The managed offering is for users who want that private workspace without administering Linux, Docker, TLS, or persistent storage themselves. First launch opens AppWeaver's web setup; configuration, credentials, installed apps, databases, and app data remain in the managed volume.
This reduced Alpine image intentionally excludes browser automation, Chromium, Cursor Agent, desktop, and VNC functionality.
Summary
Validation
ghcr.io/getappweaver/core:alpineat digestsha256:e36f3d29fc3cac7a7bc1f9ab5708e67ad6c52e800aaa751aade516cafe70e3fecargo test -p lnvps_compose -- --test-threads=1(54 passed)cargo run -q -p lnvps_compose --bin compose-validate -- catalog/*.yaml(all eight documents passed)cargo test -p lnvps_operator catalog_documents_map_to_k8s -- --test-threads=1HOST_PORT_OFFSET=11000 ./scripts/app-catalog-test.sh catalog/appweaver.yamlagainst the published GHCR image/api/health200, setup redirect 302, Piper ready, persistent workspace populated