@malloy-publisher/server 0.0.382-dev → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.docker.md +145 -20
- package/README.md +65 -12
- package/dist/app/api-doc.html +5 -0
- package/dist/app/api-doc.yaml +3903 -583
- package/dist/app/assets/EnvironmentPage-DiUgLuCH.js +1 -0
- package/dist/app/assets/HomePage-MhhjhnFj.js +1 -0
- package/dist/app/assets/LightMode-BEyvNUzT.js +1 -0
- package/dist/app/assets/MainPage-D4Cfee9o.js +2 -0
- package/dist/app/assets/MaterializationsPage-nGgGqwSN.js +1 -0
- package/dist/app/assets/ModelPage-DLAcEw-F.js +1 -0
- package/dist/app/assets/PackagePage-Df0ikH7w.js +1 -0
- package/dist/app/assets/RouteError-BFQwtroH.js +1 -0
- package/dist/app/assets/ThemeEditorPage-OoskoQYN.js +1 -0
- package/dist/app/assets/WorkbookPage-CpFgvUDC.js +1 -0
- package/dist/app/assets/{core-CjeTkq8O.es-BqRc6yhC.js → core-lb28vQkR.es-BoVSOc75.js} +1 -1
- package/dist/app/assets/github-dark-DenFmJkN.es-DHJKELXO.js +1 -0
- package/dist/app/assets/index-5eLCcNmP.css +1 -0
- package/dist/app/assets/index-6ypuFH6g.js +1774 -0
- package/dist/app/assets/index-B8pnU_V_.js +615 -0
- package/dist/app/assets/index-BImcHmVP.js +1 -0
- package/dist/app/assets/index-DSoCUF-f.js +2725 -0
- package/dist/app/assets/index-DfqzSItA.js +2 -0
- package/dist/app/assets/index-w_0OQJgZ.js +23 -0
- package/dist/app/index.html +8 -4
- package/dist/cpufeatures-1yrn0vtw.node +0 -0
- package/dist/default-publisher.config.json +23 -0
- package/dist/{instrumentation.js → instrumentation.mjs} +10631 -10573
- package/dist/package_load_worker.mjs +15219 -0
- package/dist/runtime/publisher.js +331 -0
- package/dist/{server.js → server.mjs} +115548 -49590
- package/dist/sshcrypto-vd2k5hq9.node +0 -0
- package/package.json +41 -28
- package/publisher.config.example.bigquery.json +33 -0
- package/publisher.config.example.duckdb.json +23 -0
- package/.eslintrc.json +0 -44
- package/.prettierignore +0 -1
- package/.prettierrc +0 -4
- package/build.ts +0 -38
- package/dist/app/assets/HomePage-Dn3E4CuB.js +0 -1
- package/dist/app/assets/MainPage-BzB3yoqi.js +0 -2
- package/dist/app/assets/ModelPage-C9O_sAXT.js +0 -1
- package/dist/app/assets/PackagePage-DcxKEjBX.js +0 -1
- package/dist/app/assets/ProjectPage-BDj307rF.js +0 -1
- package/dist/app/assets/RouteError-DAShbVCG.js +0 -1
- package/dist/app/assets/WorkbookPage-Cs_XYEaB.js +0 -1
- package/dist/app/assets/index-15BOvhp0.js +0 -456
- package/dist/app/assets/index-Bb2jqquW.js +0 -676
- package/dist/app/assets/index-CMlGQMcl.css +0 -1
- package/dist/app/assets/index-D68X76-7.js +0 -1276
- package/dist/app/assets/index.umd-DGBekgSu.js +0 -1145
- package/dxt/malloy_bridge.py +0 -354
- package/dxt/manifest.json +0 -22
- package/eslint.config.mjs +0 -76
- package/k6-tests/README.md +0 -144
- package/k6-tests/bun.lock +0 -797
- package/k6-tests/load-test/load-test-crud-connections.ts +0 -263
- package/k6-tests/load-test/load-test-crud-packages.ts +0 -461
- package/k6-tests/load-test/load-test-crud-projects.ts +0 -160
- package/k6-tests/load-test/load-test-crud.ts +0 -142
- package/k6-tests/load-test/load-test.ts +0 -784
- package/k6-tests/package.json +0 -27
- package/k6-tests/scripts/run-with-otel.sh +0 -50
- package/k6-tests/smoke-test/smoke-test.ts +0 -158
- package/k6-tests/tsconfig.json +0 -29
- package/k6-tests/utils/client_factory.ts +0 -128
- package/k6-tests/utils/common.ts +0 -489
- package/k6-tests/utils/logger.ts +0 -62
- package/k6-tests/utils/types.d.ts +0 -35
- package/malloy_mcp.dxt +0 -0
- package/publisher.config.json +0 -33
- package/src/config.spec.ts +0 -850
- package/src/config.ts +0 -305
- package/src/constants.ts +0 -10
- package/src/controller/compile.controller.ts +0 -35
- package/src/controller/connection.controller.ts +0 -515
- package/src/controller/database.controller.ts +0 -21
- package/src/controller/manifest.controller.ts +0 -29
- package/src/controller/materialization.controller.ts +0 -125
- package/src/controller/model.controller.ts +0 -107
- package/src/controller/package.controller.ts +0 -182
- package/src/controller/query.controller.ts +0 -60
- package/src/controller/watch-mode.controller.ts +0 -98
- package/src/data_styles.ts +0 -71
- package/src/dto/connection.dto.spec.ts +0 -131
- package/src/dto/connection.dto.ts +0 -174
- package/src/dto/index.ts +0 -2
- package/src/dto/package.dto.spec.ts +0 -42
- package/src/dto/package.dto.ts +0 -12
- package/src/dto/validate.spec.ts +0 -76
- package/src/dto/validate.ts +0 -31
- package/src/errors.ts +0 -116
- package/src/health.ts +0 -189
- package/src/instrumentation.ts +0 -148
- package/src/logger.ts +0 -154
- package/src/mcp/error_messages.ts +0 -199
- package/src/mcp/handler_utils.ts +0 -239
- package/src/mcp/mcp_constants.ts +0 -22
- package/src/mcp/prompts/handlers.ts +0 -84
- package/src/mcp/prompts/index.ts +0 -11
- package/src/mcp/prompts/prompt_definitions.ts +0 -160
- package/src/mcp/prompts/prompt_service.ts +0 -67
- package/src/mcp/prompts/utils.ts +0 -62
- package/src/mcp/resource_metadata.ts +0 -47
- package/src/mcp/resources/model_resource.ts +0 -153
- package/src/mcp/resources/notebook_resource.ts +0 -137
- package/src/mcp/resources/package_resource.ts +0 -370
- package/src/mcp/resources/project_resource.ts +0 -184
- package/src/mcp/resources/query_resource.ts +0 -117
- package/src/mcp/resources/source_resource.ts +0 -141
- package/src/mcp/resources/view_resource.ts +0 -136
- package/src/mcp/server.ts +0 -62
- package/src/mcp/tools/discovery_tools.ts +0 -258
- package/src/mcp/tools/execute_query_tool.ts +0 -265
- package/src/server.ts +0 -1195
- package/src/service/connection.spec.ts +0 -1331
- package/src/service/connection.ts +0 -1511
- package/src/service/connection_service.spec.ts +0 -641
- package/src/service/connection_service.ts +0 -181
- package/src/service/db_utils.spec.ts +0 -714
- package/src/service/db_utils.ts +0 -1227
- package/src/service/filter.spec.ts +0 -447
- package/src/service/filter.ts +0 -337
- package/src/service/filter_integration.spec.ts +0 -825
- package/src/service/gcs_s3_utils.ts +0 -427
- package/src/service/manifest_service.spec.ts +0 -201
- package/src/service/manifest_service.ts +0 -106
- package/src/service/materialization_service.spec.ts +0 -648
- package/src/service/materialization_service.ts +0 -929
- package/src/service/materialized_table_gc.spec.ts +0 -383
- package/src/service/materialized_table_gc.ts +0 -279
- package/src/service/model.spec.ts +0 -257
- package/src/service/model.ts +0 -1069
- package/src/service/package.spec.ts +0 -236
- package/src/service/package.ts +0 -463
- package/src/service/project.ts +0 -681
- package/src/service/project_compile.spec.ts +0 -197
- package/src/service/project_store.spec.ts +0 -859
- package/src/service/project_store.ts +0 -1531
- package/src/service/quoting.ts +0 -41
- package/src/service/resolve_project.ts +0 -13
- package/src/storage/BaseRepository.ts +0 -31
- package/src/storage/DatabaseInterface.ts +0 -180
- package/src/storage/StorageManager.mock.ts +0 -59
- package/src/storage/StorageManager.ts +0 -210
- package/src/storage/duckdb/ConnectionRepository.ts +0 -141
- package/src/storage/duckdb/DuckDBConnection.ts +0 -177
- package/src/storage/duckdb/DuckDBManifestStore.ts +0 -70
- package/src/storage/duckdb/DuckDBRepository.ts +0 -224
- package/src/storage/duckdb/ManifestRepository.ts +0 -119
- package/src/storage/duckdb/MaterializationRepository.ts +0 -249
- package/src/storage/duckdb/PackageRepository.ts +0 -135
- package/src/storage/duckdb/ProjectRepository.ts +0 -148
- package/src/storage/duckdb/manifest_store.spec.ts +0 -133
- package/src/storage/duckdb/schema.ts +0 -147
- package/src/storage/ducklake/DuckLakeManifestStore.ts +0 -146
- package/src/utils.ts +0 -13
- package/tests/fixtures/persist-test/data/orders.csv +0 -5
- package/tests/fixtures/persist-test/persist_test.malloy +0 -11
- package/tests/fixtures/persist-test/publisher.json +0 -5
- package/tests/fixtures/publisher.config.json +0 -15
- package/tests/harness/e2e.ts +0 -96
- package/tests/harness/mcp_test_setup.ts +0 -344
- package/tests/harness/mocks.ts +0 -37
- package/tests/harness/rest_e2e.ts +0 -68
- package/tests/harness/uris.ts +0 -31
- package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +0 -470
- package/tests/integration/mcp/mcp_execute_query_tool.integration.spec.ts +0 -434
- package/tests/integration/mcp/mcp_resource.integration.spec.ts +0 -663
- package/tests/integration/mcp/mcp_transport.integration.spec.ts +0 -122
- package/tests/integration/mcp/setup.spec.ts +0 -5
- package/tests/unit/duckdb/attached_databases.test.ts +0 -1132
- package/tests/unit/ducklake/ducklake.test.ts +0 -950
- package/tests/unit/mcp/prompt_definitions.test.ts +0 -102
- package/tests/unit/mcp/prompt_happy.test.ts +0 -51
- package/tsconfig.json +0 -32
package/README.docker.md
CHANGED
|
@@ -1,32 +1,157 @@
|
|
|
1
|
-
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) Credible Data Inc.
|
|
3
|
+
SPDX-License-Identifier: MIT
|
|
4
|
+
-->
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
# Publisher in Docker
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
The canonical build is the root [`Dockerfile`](../../Dockerfile) and the CI smoke test (`docker_smoke_test` in `.github/workflows/build.yml`) builds and runs that exact image. The two-port REST + MCP server, the Snowflake ADBC driver, the DuckDB CLI, and the production app bundle all ship in it.
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
A short Docker section in the [deployment guide](../../docs/deployment.md) covers the canonical build + run; this doc goes deeper on runtime layout, environment variables, persistent storage, and credentials.
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
## Build and run
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
```bash
|
|
15
|
+
docker build -t malloy-publisher .
|
|
16
|
+
docker run -d \
|
|
17
|
+
--name malloy-publisher \
|
|
18
|
+
-p 4000:4000 -p 4040:4040 \
|
|
19
|
+
-v $(pwd)/publisher.config.json:/publisher/publisher.config.json:ro \
|
|
20
|
+
malloy-publisher
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Once `/api/v0/status` reports `operationalState: "serving"`, the REST API is at `http://localhost:4000` and MCP at `http://localhost:4040/mcp`.
|
|
24
|
+
|
|
25
|
+
If you don't have a config of your own yet, copy [`packages/server/publisher.config.example.duckdb.json`](./publisher.config.example.duckdb.json) (DuckDB-only samples, no credentials required) and mount that. There's also a [`publisher.config.example.bigquery.json`](./publisher.config.example.bigquery.json) sibling for the BigQuery samples.
|
|
26
|
+
|
|
27
|
+
## Pre-built image
|
|
28
|
+
|
|
29
|
+
If you don't want to build the image yourself, the official pre-built image is published to Docker Hub under the **`ms2data/`** namespace (not `malloydata/`):
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
docker pull ms2data/malloy-publisher
|
|
33
|
+
docker run -d \
|
|
34
|
+
--name malloy-publisher \
|
|
35
|
+
-p 4000:4000 -p 4040:4040 \
|
|
36
|
+
-v $(pwd)/publisher.config.json:/publisher/publisher.config.json:ro \
|
|
37
|
+
ms2data/malloy-publisher
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
See the [Docker Hub tags page](https://hub.docker.com/r/ms2data/malloy-publisher/tags) for available versions. Tag-scheme guidance (`:latest`, `:X.Y.Z`, `:next`) lives in the [deployment guide](../../docs/deployment.md).
|
|
41
|
+
|
|
42
|
+
## Runtime layout
|
|
43
|
+
|
|
44
|
+
| Path inside container | What's there |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `/publisher/` | `WORKDIR`. The server reads `<WORKDIR>/publisher.config.json` by default — that's the file you mount. |
|
|
47
|
+
| `/publisher/packages/server/dist/` | The bundled server (built by `bun run build` in CI). |
|
|
48
|
+
| `/publisher/packages/app/dist/` | The static SPA the server serves. |
|
|
49
|
+
| `/publisher/publisher_data/` | Per-environment package clones, DuckDB extension cache, and per-package sandbox DBs. Created at runtime; **persist this as a named volume if you want first-run sample clones to survive a container restart.** |
|
|
50
|
+
| `/root/.duckdb/` | DuckDB CLI + extension install dir. Bundled into the image. |
|
|
51
|
+
|
|
52
|
+
To keep `publisher_data/` across restarts:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
docker run -d \
|
|
56
|
+
--name malloy-publisher \
|
|
57
|
+
-p 4000:4000 -p 4040:4040 \
|
|
58
|
+
-v $(pwd)/publisher.config.json:/publisher/publisher.config.json:ro \
|
|
59
|
+
-v publisher_data:/publisher/publisher_data \
|
|
60
|
+
malloy-publisher
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The first request after a fresh start clones sample packages from GitHub — a named volume turns that one-time cost into a one-time cost across all container lifecycles.
|
|
12
64
|
|
|
13
|
-
|
|
14
|
-
docker build -t malloy-publisher:latest -f docker/production.docker .
|
|
15
|
-
docker build -t malloy-samples:latest -f docker/malloy-samples.docker .
|
|
16
|
-
```
|
|
65
|
+
For the same pattern as a complete Compose file (with a healthcheck against `/api/v0/status` and both ports mapped), see [`docker-compose.example.yml`](../../docker-compose.example.yml) at the repo root.
|
|
17
66
|
|
|
18
|
-
|
|
19
|
-
The Publisher server runs at port 4000, the MCP server at port 4040.
|
|
67
|
+
## Configuration via environment variables
|
|
20
68
|
|
|
21
|
-
|
|
22
|
-
docker run -p 4000:4000 -p 4040:4040 malloy-samples:latest
|
|
23
|
-
```
|
|
69
|
+
All flags exposed by `bin/malloy-publisher --help` have an equivalent env var, so they're easy to set from `docker run -e` or compose:
|
|
24
70
|
|
|
25
|
-
|
|
26
|
-
|
|
71
|
+
| Env var | Equivalent flag | Default | Purpose |
|
|
72
|
+
|---|---|---|---|
|
|
73
|
+
| `PUBLISHER_PORT` | `--port <n>` | `4000` | REST API port. |
|
|
74
|
+
| `PUBLISHER_HOST` | `--host <h>` | `0.0.0.0` | Bind address. |
|
|
75
|
+
| `MCP_PORT` | `--mcp_port <n>` | `4040` | MCP API port. |
|
|
76
|
+
| `PUBLISHER_NO_MCP_CONFIG` | `--no-mcp-config` | `1` **in this image** | Suppresses the `.mcp.json` the server otherwise writes into its working directory on startup. That file exists so an AI agent opened in that directory finds the server; nothing starts an agent session inside the container, and the git-working-tree guard that would normally cover `/publisher` cannot fire because `.dockerignore` excludes `.git`. Left on, every boot would create a root-owned file, which matters if you bind-mount a project directory at `/publisher`. Pass `-e PUBLISHER_NO_MCP_CONFIG=` to turn it back on. Note this is the one env var the image sets for you: `docker run -e PUBLISHER_NO_MCP_CONFIG` (no `=`) and a Compose `environment:` entry with no value both *delete* it when the host does not have it set, which re-enables the write. |
|
|
77
|
+
| `SERVER_ROOT` | `--server_root <path>` | `.` (cwd) at the server level; overridden to `/publisher` by the bundled CMD | Directory the server treats as its working dir. The image's CMD passes `--server_root /publisher` explicitly so the zero-arg `npx` bundled-default trigger doesn't fire inside the container. If you override CMD with your own entrypoint, set `SERVER_ROOT` yourself to keep this behaviour. |
|
|
78
|
+
| `PUBLISHER_CONFIG_PATH` | `--config <path>` | unset | Absolute path to a `publisher.config.json`. Wins over `<SERVER_ROOT>/publisher.config.json`. Use this if you want to mount your config somewhere other than `/publisher/`. |
|
|
79
|
+
| `INITIALIZE_STORAGE` | `--init` | `false` | Wipes `publisher_data/` and re-syncs it from the config on boot. A first boot with empty storage loads the config automatically, so set this only to reset state or resync after the on-disk config has drifted from `publisher_data/`. Re-initializing discards any state there that isn't reproducible from the config. See [configuration.md](../../docs/configuration.md#environment-variables--cli-flags). |
|
|
80
|
+
| `SHUTDOWN_DRAIN_DURATION_SECONDS` | `--shutdown_drain_duration_seconds <s>` | `0` | On SIGTERM, how long to keep serving requests (readiness flips to not-ready immediately) before closing server sockets. Set this to your typical request duration to avoid 502s from K8s rolling deploys. |
|
|
81
|
+
| `SHUTDOWN_GRACEFUL_CLOSE_TIMEOUT_SECONDS` | `--shutdown_graceful_close_timeout_seconds <s>` | `0` | Additional grace period after server close before `process.exit`. |
|
|
82
|
+
| `GOOGLE_APPLICATION_CREDENTIALS` | — | unset | Path inside the container to a GCP service-account JSON. Required for BigQuery-backed environments. Personal user credentials don't work inside the container — use a service account. |
|
|
83
|
+
| `PUBLISHER_MAX_MEMORY_BYTES` | — | unset (disabled) | Resident-set-size (RSS) cap in bytes. When set, the in-process **memory governor** polls RSS on `PUBLISHER_MEMORY_CHECK_INTERVAL_MS` and rejects new package loads and new queries with **HTTP 503** once RSS crosses the high-water mark. Designed to keep the pod under its k8s `resources.limits.memory` instead of getting OOM-killed. Set this to roughly `0.7 × resources.limits.memory` so the back-pressure band has headroom for traffic spikes and per-request DuckDB scratch. |
|
|
84
|
+
| `PUBLISHER_MEMORY_HIGH_WATER_FRACTION` | — | `0.8` | Fraction of `PUBLISHER_MAX_MEMORY_BYTES` at which back-pressure activates. Must be in `(0, 1)` and strictly greater than the low-water fraction. |
|
|
85
|
+
| `PUBLISHER_MEMORY_LOW_WATER_FRACTION` | — | `0.7` | Fraction at which back-pressure clears. The gap between low and high gives hysteresis so the governor doesn't flap on every GC cycle. |
|
|
86
|
+
| `PUBLISHER_MEMORY_CHECK_INTERVAL_MS` | — | `5000` | How often the governor samples RSS. Minimum `100`. Smaller values catch spikes faster but burn a few extra microseconds per tick. |
|
|
87
|
+
| `PUBLISHER_MEMORY_BACKPRESSURE` | — | `true` | When `false`, the governor still samples RSS and emits metrics but never flips the back-pressure flag. Useful for a monitoring-only rollout before enabling the 503 behaviour. |
|
|
88
|
+
| `EMBEDDING_API_KEY` | — | _unset_ | Enables semantic ranking for `malloy_getContext` question retrieval; sent as a bearer token to the embedding endpoint. Unset keeps lexical retrieval, unchanged. Entity names, annotation text, and query strings are sent to the endpoint when enabled; see "Semantic retrieval" in `docs/configuration.md`. |
|
|
89
|
+
| `EMBEDDING_MODEL` | — | `text-embedding-3-small` | Embedding model name. |
|
|
90
|
+
| `EMBEDDING_API_BASE` | — | `https://api.openai.com/v1` | Base URL of an OpenAI-compatible embeddings API. |
|
|
91
|
+
| `EMBEDDING_DIMENSIONS` | — | _unset_ | Optional `dimensions` request parameter; omitted when unset. |
|
|
92
|
+
|
|
93
|
+
### Memory governor
|
|
94
|
+
|
|
95
|
+
When `PUBLISHER_MAX_MEMORY_BYTES` is unset, the governor is **disabled** and the server's behaviour is identical to prior versions. When it's set, the governor:
|
|
96
|
+
|
|
97
|
+
- Periodically samples `process.memoryUsage().rss`.
|
|
98
|
+
- Once RSS crosses the high-water mark, **any code path that would allocate a new package into memory returns HTTP 503**, and new queries are rejected the same way. The package gate sits at the single choke point inside `Environment.getPackage` / `Environment.addPackage`, so it covers every controller that touches a not-yet-loaded package — including lazy loads on cache miss from `ModelController`, `ConnectionController`, `QueryController`, `DatabaseController`, etc. — not just the explicit `POST /packages` and `?reload=true` paths.
|
|
99
|
+
- Already-loaded packages remain fully serviceable so dashboards keep rendering under pressure.
|
|
100
|
+
- Once RSS drops back to the low-water mark, back-pressure clears automatically.
|
|
101
|
+
- Recovery happens naturally as in-flight traffic completes and the kernel reclaims pages — the governor does **not** evict, unload, or interrupt loaded packages.
|
|
102
|
+
- A documented `{ allowAdmission: true }` opt-out exists on `Environment.getPackage` / `addPackage` for future internal callers (e.g. warmup / health probes) that genuinely cannot tolerate 503s. No public REST endpoint sets it today.
|
|
103
|
+
|
|
104
|
+
Metrics exposed on the existing `/metrics` Prometheus endpoint:
|
|
105
|
+
|
|
106
|
+
| Metric | Type | Notes |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| `publisher_process_rss_bytes` | gauge | Sampled RSS. |
|
|
109
|
+
| `publisher_memory_backpressure_active` | gauge | `1` when rejecting new loads, `0` otherwise. |
|
|
110
|
+
| `publisher_memory_backpressure_activations_total` | counter | Increments on every `false → true` transition; alert on a non-trivial rate to catch flapping pods. |
|
|
111
|
+
| `publisher_memory_max_bytes`, `publisher_memory_high_water_bytes`, `publisher_memory_low_water_bytes` | gauges | Static configured thresholds — useful for plotting the band alongside the RSS series. |
|
|
112
|
+
|
|
113
|
+
#### Recommended k8s sizing
|
|
114
|
+
|
|
115
|
+
A reasonable starting point (tune for your workload):
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
resources:
|
|
119
|
+
requests:
|
|
120
|
+
memory: 2Gi
|
|
121
|
+
limits:
|
|
122
|
+
memory: 4Gi
|
|
123
|
+
env:
|
|
124
|
+
- name: PUBLISHER_MAX_MEMORY_BYTES
|
|
125
|
+
# 2.8Gi — back-pressure activates at ~2.24Gi, clears at ~1.96Gi,
|
|
126
|
+
# leaving ~1.2Gi of headroom under the 4Gi k8s hard limit for
|
|
127
|
+
# in-flight DuckDB scratch and JS heap spikes.
|
|
128
|
+
value: "3006477107"
|
|
129
|
+
- name: PUBLISHER_MEMORY_BACKPRESSURE
|
|
130
|
+
value: "true"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
If you want a soft-launch where the governor reports but doesn't act, deploy first with `PUBLISHER_MEMORY_BACKPRESSURE=false`, watch the `publisher_process_rss_bytes` series for a week, then enable.
|
|
134
|
+
|
|
135
|
+
## BigQuery credentials
|
|
136
|
+
|
|
137
|
+
To enable BigQuery samples or your own BigQuery connections, mount a service-account key and point `GOOGLE_APPLICATION_CREDENTIALS` at it:
|
|
27
138
|
|
|
28
139
|
```bash
|
|
29
|
-
docker run -
|
|
30
|
-
|
|
31
|
-
|
|
140
|
+
docker run -d \
|
|
141
|
+
--name malloy-publisher \
|
|
142
|
+
-p 4000:4000 -p 4040:4040 \
|
|
143
|
+
-v $(pwd)/publisher.config.json:/publisher/publisher.config.json:ro \
|
|
144
|
+
-v $(pwd)/gcp-sa.json:/etc/publisher/gcp-sa.json:ro \
|
|
145
|
+
-e GOOGLE_APPLICATION_CREDENTIALS=/etc/publisher/gcp-sa.json \
|
|
146
|
+
malloy-publisher
|
|
32
147
|
```
|
|
148
|
+
|
|
149
|
+
The Dockerfile creates `/etc/publisher/` as an empty directory outside the application tree at `/publisher/`. By the convention this doc establishes, mount credential material there to keep it separated from the app — but any writable path inside the container works.
|
|
150
|
+
|
|
151
|
+
## The CI Dockerfile (`docker/Dockerfile.ci`)
|
|
152
|
+
|
|
153
|
+
`docker/Dockerfile.ci` exists for the CI integration-test path (referenced from the repo's `docker-compose.yml`). It is **not** the production image and should not be used for deployment. Production users build the root [`Dockerfile`](../../Dockerfile).
|
|
154
|
+
|
|
155
|
+
## Deprecated build paths
|
|
156
|
+
|
|
157
|
+
`docker/production.docker` and `docker/malloy-samples.docker` are leftover from a previous Docker layout. They are not built by CI, are not referenced by any current workflow, and produce a different image than what is deployed. Don't use them — build the root [`Dockerfile`](../../Dockerfile) instead. They will be removed in a follow-up cleanup PR.
|
package/README.md
CHANGED
|
@@ -1,10 +1,72 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Copyright (c) Credible Data Inc.
|
|
3
|
+
SPDX-License-Identifier: MIT
|
|
4
|
+
-->
|
|
5
|
+
|
|
1
6
|
# Malloy Publisher Server
|
|
2
7
|
|
|
3
8
|
The Malloy Publisher Server is an Express.js server that provides an API for managing and accessing Malloy data models, packages, and queries
|
|
4
9
|
|
|
10
|
+
## Quick start
|
|
11
|
+
|
|
12
|
+
This section is self-contained: this README ships in the npm tarball, where most of the repository does not, so nothing here needs a file you do not have. Later sections are written for a clone and do point into the repository.
|
|
13
|
+
|
|
14
|
+
**New workspace?** One command scaffolds a package, the server config, the MCP wiring, and the agent skills:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm create @malloy-publisher/malloy-package@latest sales
|
|
18
|
+
npm start
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Run bare like that, the package comes with a small sample dataset, so there is something to query immediately. To start from a file of your own instead, add `-- --data ./orders.csv`, naming a delimited or Parquet/Excel file you actually have (the `--` is required, and the path is relative to where you run the command). Keep the `@latest`: `npm create` resolves through the npx cache, and an unversioned name silently reuses whatever old copy is on the machine.
|
|
22
|
+
|
|
23
|
+
**Existing directory of models?** Write a `publisher.config.json` beside a package directory that holds a `publisher.json`, then run the server pointing at it:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx @malloy-publisher/server --server_root . --watch-env local
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"frozenConfig": false,
|
|
32
|
+
"environments": [
|
|
33
|
+
{
|
|
34
|
+
"name": "local",
|
|
35
|
+
"packages": [{ "name": "sales", "location": "./sales" }],
|
|
36
|
+
"connections": []
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The config is read from `<server_root>/publisher.config.json` by default; `--config <path>` overrides it.
|
|
43
|
+
|
|
44
|
+
Three facts that are easy to get wrong:
|
|
45
|
+
|
|
46
|
+
- **A flat-file (CSV/Parquet/XLSX) package needs no `connections` entry at all.** Every loaded package automatically gets its own DuckDB sandbox connection named `duckdb`, which is what `duckdb.table('data/file.csv')` resolves against. That name is reserved: an environment-level connection named `duckdb` fails the whole environment at init (call an env-level DuckDB connection `shared_duckdb` or similar).
|
|
47
|
+
- **A package `location` is local only when it starts with `./`, `../`, `~/`, or `/`.** Write `./sales`, not `sales`: a bare name is read as a remote URI, and the package is skipped with `Invalid package path: "sales". Must be an absolute mounted path or a GCS/S3/GitHub URI.` in `loadErrors`. A `./` or `../` path resolves against the config file's directory; `~/` against your home directory.
|
|
48
|
+
- **Local authoring means `--watch-env <env>`.** Without it the server copies each local package into `publisher_data/` at boot and serves the copy, so edits to your source directory are never read — a reload recompiles the copy and still answers 200. Adding the flag to a later boot does not undo a copy already made: pass `--watch-env <env> --init` once to re-mount (it wipes `publisher_data/` and re-syncs from config), then plain `--watch-env` boots keep watching. `publisher_data/<env>/<pkg>` tells you which you got — a symlink is mounted, a real directory is a copy.
|
|
49
|
+
|
|
50
|
+
Poll `curl -s http://localhost:4000/api/v0/status` until `operationalState` is `"serving"`, then check `loadErrors` (absent when everything loaded). The MCP endpoint for agents is `http://localhost:4040/mcp`.
|
|
51
|
+
|
|
52
|
+
## Configuration
|
|
53
|
+
|
|
54
|
+
Two example configs ship in the npm package, beside this README: [`publisher.config.example.duckdb.json`](./publisher.config.example.duckdb.json) (the GitHub-hosted sample packages, no connection block) and [`publisher.config.example.bigquery.json`](./publisher.config.example.bigquery.json) (adds a BigQuery connection). Copy either one to `publisher.config.json` and point `--server_root` at its directory.
|
|
55
|
+
|
|
56
|
+
In a clone, the live `publisher.config.json` lives in this directory, and the repository root contains a symlink (`/publisher.config.json` → `./packages/server/publisher.config.json`) so that running the server from either location picks up the same config. Edit one and you've edited both.
|
|
57
|
+
|
|
58
|
+
### Remaining deprecation warnings
|
|
59
|
+
|
|
60
|
+
Removing the unused `trino` CLI direct dep (it pulled in `@google-cloud/translate@0.7.x` → `request@2.x` → `har-validator` → `hawk` → `cryptiles`) cleaned the worst chain. About 25 `npm warn deprecated` lines remain on `npx @malloy-publisher/server` install, all upstream-owned:
|
|
61
|
+
|
|
62
|
+
- **npm CLI tooling**: `npmlog`, `gauge`, `are-we-there-yet`, `glob@7/8/10`, `rimraf@3`, `tar@6.2.1`, `inflight`, `@npmcli/move-file`, `node-domexception`, `querystring` — pulled in by npm itself and by `node-pre-gyp`/`node-gyp`. Not actionable from this repo.
|
|
63
|
+
- **`uuid@8.x` / `uuid@9.x`**: surfaced across multiple transitives (Malloy, AWS SDKs, others). Resolves when each upstream bumps to `uuid@11`.
|
|
64
|
+
- **`q@1.5.1`**: pulled in via `thrift` → `@databricks/sql` → `@malloydata/db-databricks`. Resolves when Databricks upgrades `@databricks/sql` past the thrift dep, or when we replace the Databricks driver.
|
|
65
|
+
- **`aws-sdk@2.1693.0`**: no longer a direct dep (removed from `packages/server/package.json`); anything still surfacing it is transitive. The actual S3 consumer is `@aws-sdk/client-s3` v3.
|
|
66
|
+
|
|
5
67
|
## K6 Test Presets
|
|
6
68
|
|
|
7
|
-
The Malloy Publisher Server includes several K6 test presets to help you test its performance and stability.
|
|
69
|
+
The Malloy Publisher Server includes several K6 test presets to help you test its performance and stability. These live in the repository, not the npm package, so run them from a clone.
|
|
8
70
|
|
|
9
71
|
Below is a list of the available test presets:
|
|
10
72
|
|
|
@@ -109,14 +171,7 @@ For more information on how to configure OpenTelemetry collectors, please refer
|
|
|
109
171
|
|
|
110
172
|
## MCP Prompt Capability
|
|
111
173
|
|
|
112
|
-
Publisher's MCP interface exposes **LLM-ready prompts
|
|
113
|
-
|
|
114
|
-
| Prompt ID | Purpose |
|
|
115
|
-
| ---------------------------------------------- | ----------------------------------------------------------------- |
|
|
116
|
-
| `explain-malloy-query@1.0.0` | Explain a Malloy query (sources, transformations, output). |
|
|
117
|
-
| `generate-malloy-query-from-description@1.0.0` | Create Malloy based on a natural-language goal and model context. |
|
|
118
|
-
| `translate-sql-to-malloy@1.0.0` | Convert SQL into Malloy using a model for schema reference. |
|
|
119
|
-
| `summarize-malloy-model@1.0.0` | Summarise a Malloy model (purpose, entities, joins). |
|
|
174
|
+
Publisher's MCP interface exposes the bundled agent **skills** as **LLM-ready prompts**, so hosts that ingest MCP but do not load skill files can pull the same guidance. The skills are published separately as [`@malloy-publisher/skills`](https://www.npmjs.com/package/@malloy-publisher/skills); their source is [`skills/`](https://github.com/malloydata/publisher/tree/main/skills), and authoring guidance is in [`docs/agent-skills/`](https://github.com/malloydata/publisher/tree/main/docs/agent-skills).
|
|
120
175
|
|
|
121
176
|
List prompts:
|
|
122
177
|
|
|
@@ -127,9 +182,7 @@ mcp-client prompts/list
|
|
|
127
182
|
Get a prompt:
|
|
128
183
|
|
|
129
184
|
```bash
|
|
130
|
-
mcp-client prompts/get
|
|
131
|
-
--name explain-malloy-query@1.0.0 \
|
|
132
|
-
--arguments '{"query_code":"from flights"}'
|
|
185
|
+
mcp-client prompts/get --name malloy-analysis
|
|
133
186
|
```
|
|
134
187
|
|
|
135
188
|
These calls return `messages` ready for your LLM chat completion.
|