Skip to content

feat(alchemy): run Maple Local inside alchemy dev - #2011

Open
ebramanti wants to merge 6 commits into
MapleTechLabs:mainfrom
herd-labs:feat/alchemy-local-runtime
Open

ebramanti wants to merge 6 commits into
MapleTechLabs:mainfrom
herd-labs:feat/alchemy-local-runtime

Conversation

@ebramanti

@ebramanti ebramanti commented Oct 31, 2026 •

Copy link
Copy Markdown

Written by an AI agent (Claude Code) on behalf of @ebramanti.

A team running Workers under alchemy dev can now see their traces and logs in Maple without a Maple account, an ingest key or a separate maple start: one Local resource runs Maple Local inside the stack, and every Worker exports to it.

Problem

On main, Telemetry only knows hosted Maple. Under alchemy dev a Worker either exports to production with a real ingest key, or runs the SDK in no-op mode and exports nothing. To see local telemetry you install the maple binary, run maple start in another terminal, and point each Worker's MAPLE_ENDPOINT at it by hand. Nothing ties that server's lifetime or data to the stack.

Fix

// Observability.ts: one declaration, shared by every Worker.
export const Observability = Local("observability")

// Api.ts
export default Cloudflare.Worker(
	"api",
	{ main: import.meta.url },
	Effect.succeed({ fetch: handler }).pipe(
		Effect.provide(Telemetry({ serviceName: "api", local: Observability })),
	),
)

// alchemy.run.ts: register localProviders() next to the other providers.
providers: Layer.mergeAll(Cloudflare.providers(), localProviders()),

Under alchemy dev, Telemetry({ local }) binds MAPLE_ENDPOINT to the local server and needs no ingest key; deploys ignore local. The dashboard is local.maple.dev pointed at the loopback server, as with maple start.

  • One server per stack. Local starts the server in an Alchemy native sidecar. Workers share it, it survives stack reloads (HMR keeps the same sidecar process), and Ctrl-C closes the store cleanly.
  • Data per stack, stage and resource. The store is .alchemy/maple-local/<sha256(stack, stage, fqn)>/data and persists across runs. LocalProps takes dataDir, port (default 4318, an occupied port fails the start) and onDirtyStore.
  • No CLI involved. The sidecar embeds the server in-process through a new startLocal() in @maple/local-server; it never spawns maple.
  • libchdb. The first run downloads the pinned chDB release, checks its sha256 before extracting, and caches it at ~/.maple/cache/libchdb/<release>/. MAPLE_LIBCHDB uses a copy you already have. The runtime checks the loaded library's chdb_version() against the pin.
  • Unclean stores. Every alchemy dev exit I could produce (Ctrl-C during ingest, closing the terminal, kill -9 of alchemy dev) closed the store cleanly; only killing the sidecar itself, an OOM or power loss leaves it dirty. These stores take no checkpoints, so there is nothing to restore: the start fails closed with a message naming onDirtyStore: "wipe", which discards the live data. Changing the prop applies in the running session.

How it's built (three commits, review in order)

  1. refactor(local-server): move the Maple Local server into @maple/local-server. A pure move: 56 files from apps/cli/src/server into a private workspace package, import rewrites, and the untangling that lets the package build without the CLI. The 41 historical schema snapshots stay in the CLI next to the migrations (keeps the sidecar about 1 MB instead of 5 MB). The PID claim, migration journal and store errors move into their own modules. A no-restricted-imports override keeps the CLI out of the package, and lint burndown entries move with their files. Error tags stay @maple/cli/* so CLI exit handling and telemetry group failures the same. No behaviour change.
  2. feat(local-server): embeddable scoped runtime with a pinned, provisioned libchdb. startLocal(); packages/local-server/libchdb.json, the one manifest scripts/build-local-binary.sh and the sidecar both read (it replaces the script's hash table); provisionLibchdb(). A failed start releases its claim even when the caller keeps its scope open, because Alchemy retries a failed resource in the same process without closing it.
  3. feat(alchemy): run Maple Local inside alchemy dev. Local, localProviders(), the sidecar, Telemetry({ local }), a two-Worker example (examples/alchemy-maple/local.alchemy.ts), docs (package README, hosted Alchemy page, docs/local-mode.md) and a minor changeset. @maple/local-server is bundled; effect, every @effect/* package and alchemy stay external so the sidecar runs on the user's own effect, with @effect/platform-bun as an optional peer (Alchemy needs it under Bun already).

tsconfig.alchemy-maple.json (from #2006) gains typeRoots so the declaration build finds @types/bun, which @maple/local-server needs.

Verification

Each commit was checked out on its own and built: typecheck, tests and lint pass at every commit.

Check Result
Typecheck: packages/local-server, apps/cli, packages/alchemy-maple, examples/alchemy-maple pass
packages/local-server tests 27 pass
apps/cli tests 761 pass, 45 skip, 0 fail
packages/alchemy-maple tests 55 pass
bun run clickhouse:schema:check, oxlint (.oxlintrc.effect.json), oxfmt pass
scripts/build-local-binary.sh on commit 2 builds; the binary starts, ingests, reports chdb: v26.7.3 from the manifest

Smoke runs on macOS arm64, Bun 1.4:

  • The in-repo example under alchemy dev: two Workers export to one server, the traces are queryable at /local/query, the sidecar PID survives a reload, data survives a restart, and Ctrl-C closes the store cleanly.
  • The packed tarball installed outside the repo with an empty HOME, on alchemy@2.0.0-beta.84 and effect@4.0.7: libchdb downloads (byte-identical to the one the release binary ships), a dirty store is refused, then setting onDirtyStore: "wipe" in the same session recovers it.

Limits

  • Bun only (bun:ffi), on macOS and Linux, x64 and arm64. Only macOS arm64 was run; the other three take the same code path with pinned hashes.
  • No periodic checkpoints, unlike maple start.
  • On effect 4.0.x a fresh install resolves @effect/platform-node-shared to 4.1.x, which breaks @effect/platform-bun@4.0.7 itself (Alchemy included). Users pin it to 4.0.7. Not something this package can fix.
  • The libchdb cache is about 330 MB per release, shared per user.

Open questions

  • Happy to split commit 1 into its own PR if that reads better.
  • This comes from a fork, so preview deploys don't run; nothing here changes the dashboard or API.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

…-server

The server, store and archive code moves from apps/cli/src/server into a
private workspace package so a second host can embed it. No behaviour
changes; the CLI imports it from @maple/local-server.

Untangled first so the package never reaches back into the CLI:
- the 41 historical schema snapshots move to apps/cli's schema-snapshots.ts,
  next to the migrations that are their only users
- the migration journal envelope, the PID claim helpers and the store
  errors get their own modules
- ensurePortFree, the chDB config path and DEFAULT_REMOTE_UI_URL move next
  to their users; serve.ts takes the telemetry layer from its caller
- archive calibration logs through a shared debug-log instead of lib/debug

Error tags stay @maple/cli/* so CLI exit handling and telemetry still group
failures by tag. A no-restricted-imports override keeps the CLI out of the
package; lint burndown entries move with their files.
…ned libchdb

startLocal({ dataDir, port?, libchdb?, onDirtyStore? }) owns one loopback
Maple Local server until its scope closes, with no CLI or maple binary
involved. It claims the store, checks the port, loads libchdb, recovers an
interrupted checkpoint, then refuses unsafe stores the way `maple start`
does. A store left unclean fails closed unless onDirtyStore is "wipe";
embedded stores have no checkpoints to restore from. A failed start
releases everything it acquired even when the caller keeps its scope
open, so a host can retry in the same process.

packages/local-server/libchdb.json pins the chDB release and each platform
asset's sha256. build-local-binary.sh reads it instead of its own table,
and provisionLibchdb() downloads the same asset, verifies it before
extraction and caches it under ~/.maple/cache/libchdb/<release>. The
runtime checks the loaded library's chdb_version() against the pin, so a
mismatched MAPLE_LIBCHDB is refused before any store is opened.
Local("observability") from @maple-dev/alchemy/local starts the embedded
@maple/local-server runtime in an Alchemy native sidecar. Telemetry({ local })
binds MAPLE_ENDPOINT to it under alchemy dev and skips the ingest key;
deploys ignore it. Every Worker in the stack shares one loopback server, the
sidecar keeps running across stack reloads, and the store lives per stack,
stage and resource under .alchemy/maple-local/<hash>/data.

The sidecar provisions libchdb from the pinned manifest unless
MAPLE_LIBCHDB is set, and LocalProps exposes dataDir, port and onDirtyStore.
@maple/local-server is bundled into the package; effect, @effect/* and
alchemy stay external, with @effect/platform-bun as an optional peer, so the
sidecar runs on the user's own effect version.

Adds a two-Worker example (examples/alchemy-maple/local.alchemy.ts), docs in
the package README and the hosted Alchemy page, and a changeset.
@maple-review-bot

maple-review-bot Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Maple review

🟡 Confidence 6/10 · needs attention. Held at 6 because 13 reviewed files were not read.
no findings · tests partial −1 · risk medium · 1/1 new units observable · 13 files not read

Moves the Maple Local server out of apps/cli into a private @maple/local-server, adds startLocal() with sha256-verified libchdb provisioning, and adds an alchemy Local resource that runs the server as a dev sidecar. Safe to merge. None of the open schema.ts/core/telemetry.ts errors are addressed: the touched lines only rewrite import paths.

  • Local runs Maple Local as an alchemy dev sidecar, one per stack, keyed by a hashed data dir
  • startLocal embeds the server in-process and releases its claim when a start fails
  • provisionLibchdb sha256-verifies the pinned chDB release before extracting it into ~/.maple/cache
  • apps/cli/src/server moves into @maple/local-server, keeping the @maple/cli/* error tags

Production impact

Open errors in the changed files (7)
Issue Service Occurrences File
ToolCallFailed (2 issues) maple-security-audit 212 apps/cli/src/commands/schema.ts
Error (2 issues) maple-web 38 apps/cli/src/commands/schema.ts
TimeoutError maple-chat 24 apps/cli/src/commands/schema.ts
ModelProtocolError maple-chat 12 apps/cli/src/commands/schema.ts
AgentPolicyError (2 issues) maple-chat 17 apps/cli/src/commands/schema.ts
Error maple-landing 8 apps/cli/src/core/telemetry.ts
ContextBudgetError maple-chat 7 apps/cli/src/commands/schema.ts

After this merges, Maple checks whether they stop.

Files not reviewed (13)

The review ended before it read these diffs, so nothing above vouches for them.

  • apps/cli/src/commands/update.ts
  • apps/cli/src/core/changelog.ts
  • apps/cli/src/core/config.ts
  • apps/cli/src/core/local-access.ts
  • apps/cli/test/archive-config.test.ts
  • apps/cli/test/archive-export-round5.test.ts
  • apps/cli/test/archive-export-safety.test.ts
  • apps/cli/test/archive-gc.test.ts
  • apps/cli/test/archive-generation.test.ts
  • apps/cli/test/archive-journal.test.ts
  • apps/cli/test/archive-legacy-calibration.test.ts
  • apps/cli/test/archive-listing.test.ts
  • apps/cli/test/archive-manifest.test.ts

e99fed8 · What was checked, coverage and telemetry details are on the Maple / review check. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@coderabbitai

coderabbitai Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 211 files, which is 61 over the limit of 150.

To get a review, reduce the PR to 150 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e57cd7f0-5731-4fa7-98ff-13e19decbc0c
Commits

Reviewing files that changed from the base of the PR and between 3c8731e and 3982433.

Files ignored due to path filters: 1
  • bun.lock is excluded by !**/*.lock
Files selected for processing: 211
  • .changeset/alchemy-local-dev.md
  • .github/workflows/ci.yml
  • .oxlintrc.json
  • apps/cli/bench/ingest-chdb.ts
  • apps/cli/package.json
  • apps/cli/src/bin.ts
  • apps/cli/src/commands/archive.ts
  • apps/cli/src/commands/changelog.ts
  • apps/cli/src/commands/delete.ts
  • apps/cli/src/commands/in-memory-store.ts
  • apps/cli/src/commands/schema.ts
  • apps/cli/src/commands/server-args.ts
  • apps/cli/src/commands/server-errors.ts
  • apps/cli/src/commands/server.ts
  • apps/cli/src/commands/start-failures.ts
  • apps/cli/src/commands/update.ts
  • apps/cli/src/core/changelog.ts
  • apps/cli/src/core/config.ts
  • apps/cli/src/core/local-access.ts
  • apps/cli/src/core/telemetry.ts
  • apps/cli/src/core/update.ts
  • apps/cli/src/lib/debug.ts
  • apps/cli/src/lib/failure.ts
  • apps/cli/src/server/local-store-migration-module.ts
  • apps/cli/src/server/local-store-migrations.ts
  • apps/cli/src/server/local-store-migrations/backfills.ts
  • apps/cli/src/server/local-store-migrations/journal-codecs.ts
  • apps/cli/src/server/local-store-migrations/legacy-to-current.ts
  • apps/cli/src/server/local-store-migrations/step-executor.ts
  • apps/cli/src/server/retained-leftovers.ts
  • apps/cli/src/server/schema-snapshots.ts
  • apps/cli/test/archive-config.test.ts
  • apps/cli/test/archive-export-round5.test.ts
  • apps/cli/test/archive-export-safety.test.ts
  • apps/cli/test/archive-gc.test.ts
  • apps/cli/test/archive-generation.test.ts
  • apps/cli/test/archive-journal.test.ts
  • apps/cli/test/archive-legacy-calibration.test.ts
  • apps/cli/test/archive-listing.test.ts
  • apps/cli/test/archive-manifest.test.ts
  • apps/cli/test/archive-path-safety.test.ts
  • apps/cli/test/archive-pins.test.ts
  • apps/cli/test/archive-probe-helpers.ts
  • apps/cli/test/archive-reconcile-decision.test.ts
  • apps/cli/test/archive-reconcile.test.ts
  • apps/cli/test/archive-retention.test.ts
  • apps/cli/test/chdb-args.test.ts
  • apps/cli/test/chdb-ingest-config.test.ts
  • apps/cli/test/chdb-timezone.test.ts
  • apps/cli/test/checkpoint-merge-freeze.test.ts
  • apps/cli/test/checkpoint-retained-days.test.ts
  • apps/cli/test/checkpoints.test.ts
  • apps/cli/test/durable-files.test.ts
  • apps/cli/test/eventing-test-support.ts
  • apps/cli/test/expected-outcomes.test.ts
  • apps/cli/test/host-filesystem-utilization.test.ts
  • apps/cli/test/in-memory-store.test.ts
  • apps/cli/test/insert-batcher.test.ts
  • apps/cli/test/inserts.test.ts
  • apps/cli/test/journal-schema.test.ts
  • apps/cli/test/local-eventing-consumer-auth.test.ts
  • apps/cli/test/local-eventing-control-store.test.ts
  • apps/cli/test/local-eventing-ingest.test.ts
  • apps/cli/test/local-eventing-overflow.test.ts
  • apps/cli/test/local-eventing-runtime.test.ts
  • apps/cli/test/local-eventing-telemetry.test.ts
  • apps/cli/test/local-ingest-rules.test.ts
  • apps/cli/test/local-query-guard.test.ts
  • apps/cli/test/local-service-map-rollup.test.ts
  • apps/cli/test/local-store-migrations.test.ts
  • apps/cli/test/local-store-service-map-rekey.test.ts
  • apps/cli/test/local-store-step-test-support.ts
  • apps/cli/test/local-store-traces-rekey.test.ts
  • apps/cli/test/maintenance-lock-recovery.test.ts
  • apps/cli/test/maintenance-operation.test.ts
  • apps/cli/test/merge-injection-probe.ts
  • apps/cli/test/native-local-store-migration-fixture.ts
  • apps/cli/test/native-local-store-migration.sh
  • apps/cli/test/native-projector-modules.ts
  • apps/cli/test/native-service-map-ingest-bridge-probe.ts
  • apps/cli/test/probes/archive-crash-worker.ts
  • apps/cli/test/probes/archive-gc-worker.ts
  • apps/cli/test/probes/archive-probe-byte-heterogeneous.ts
  • apps/cli/test/probes/archive-probe-byte-single-row.ts
  • apps/cli/test/probes/archive-probe-byte-uniform.ts
  • apps/cli/test/probes/archive-probe-complex-alter.ts
  • apps/cli/test/probes/archive-probe-digest-column-swap.ts
  • apps/cli/test/probes/archive-probe-digest-dup-drop.ts
  • apps/cli/test/probes/archive-probe-digest-row-swap.ts
  • apps/cli/test/probes/archive-probe-duckdb-oracle.ts
  • apps/cli/test/probes/archive-probe-merge-freeze-leak.ts
  • apps/cli/test/probes/archive-probe-merge-injection.ts
  • apps/cli/test/probes/archive-probe-mixed-hour.ts
  • apps/cli/test/probes/archive-probe-multipart.ts
  • apps/cli/test/probes/archive-probe-null-digest.ts
  • apps/cli/test/probes/archive-probe-null-flag-binding.ts
  • apps/cli/test/probes/archive-probe-null-value-sensitivity.ts
  • apps/cli/test/probes/archive-probe-schema-substitution.ts
  • apps/cli/test/probes/archive-probe-timezone-bound.ts
  • apps/cli/test/probes/archive-reconcile-worker.ts
  • apps/cli/test/probes/maintenance-sigint-worker.ts
  • apps/cli/test/remote-auth.test.ts
  • apps/cli/test/retained-leftovers.test.ts
  • apps/cli/test/scoped-delete.test.ts
  • apps/cli/test/serve-asset-headers.test.ts
  • apps/cli/test/serve-describe-thrown.test.ts
  • apps/cli/test/server-args.test.ts
  • apps/cli/test/server-network.test.ts
  • apps/cli/test/server-pid.test.ts
  • apps/cli/test/start-failures.test.ts
  • apps/cli/test/store-marker-schema.test.ts
  • apps/cli/test/store-version.test.ts
  • apps/cli/test/tenant-isolation.test.ts
  • apps/landing/src/components/live/LiveLocalTerminal.astro
  • apps/landing/src/content/docs/iac/alchemy.md
  • docs/eventing-extension-guide.md
  • docs/local-event-consumers.md
  • docs/local-mode.md
  • docs/local-telemetry-archives.md
  • docs/profiling.md
  • docs/service-map-architecture.md
  • docs/signal-to-event-projection.md
  • examples/alchemy-maple/README.md
  • examples/alchemy-maple/local.alchemy.ts
  • examples/alchemy-maple/src/LocalApi.ts
  • examples/alchemy-maple/src/LocalJobs.ts
  • examples/alchemy-maple/src/Observability.ts
  • packages/alchemy-maple/README.md
  • packages/alchemy-maple/package.json
  • packages/alchemy-maple/src/Local.ts
  • packages/alchemy-maple/src/LocalConfig.ts
  • packages/alchemy-maple/src/LocalSidecar.ts
  • packages/alchemy-maple/src/Telemetry.ts
  • packages/alchemy-maple/test/local.test.ts
  • packages/alchemy-maple/test/telemetry.test.ts
  • packages/alchemy-maple/tsconfig.json
  • packages/alchemy-maple/tsdown.config.ts
  • packages/eventing-core/README.md
  • packages/local-server/libchdb.json
  • packages/local-server/package.json
  • packages/local-server/src/archives/config.ts
  • packages/local-server/src/archives/errors.ts
  • packages/local-server/src/archives/export.ts
  • packages/local-server/src/archives/gc.ts
  • packages/local-server/src/archives/generation.ts
  • packages/local-server/src/archives/journal.ts
  • packages/local-server/src/archives/legacy-calibration.ts
  • packages/local-server/src/archives/listing.ts
  • packages/local-server/src/archives/manifest.ts
  • packages/local-server/src/archives/paths.ts
  • packages/local-server/src/archives/reconcile.ts
  • packages/local-server/src/archives/retention.ts
  • packages/local-server/src/archives/schemas.ts
  • packages/local-server/src/archives/signals.ts
  • packages/local-server/src/assets.d.ts
  • packages/local-server/src/chdb-rows.ts
  • packages/local-server/src/chdb.ts
  • packages/local-server/src/checkpoint-digest.ts
  • packages/local-server/src/checkpoints.ts
  • packages/local-server/src/debug-log.ts
  • packages/local-server/src/durable-files.ts
  • packages/local-server/src/eventing/consumer-auth.ts
  • packages/local-server/src/eventing/control-store.ts
  • packages/local-server/src/eventing/otlp.ts
  • packages/local-server/src/eventing/runtime.ts
  • packages/local-server/src/eventing/telemetry.ts
  • packages/local-server/src/identity-schema.ts
  • packages/local-server/src/ingest-rules.ts
  • packages/local-server/src/insert-batcher.ts
  • packages/local-server/src/inserts.ts
  • packages/local-server/src/libchdb-provision.ts
  • packages/local-server/src/local-address.ts
  • packages/local-server/src/local-schema-history.ts
  • packages/local-server/src/local-schema-version.ts
  • packages/local-server/src/local-store-migration-journal.ts
  • packages/local-server/src/local-store-migrations/journal-schema.ts
  • packages/local-server/src/local-token.ts
  • packages/local-server/src/otlp/encode.test.ts
  • packages/local-server/src/otlp/encode.ts
  • packages/local-server/src/otlp/proto.ts
  • packages/local-server/src/query-guard.ts
  • packages/local-server/src/remote-auth.ts
  • packages/local-server/src/runtime.ts
  • packages/local-server/src/schema-identity.ts
  • packages/local-server/src/schema-manifest.ts
  • packages/local-server/src/schema-physical.ts
  • packages/local-server/src/schema/control-schema-v1.sql
  • packages/local-server/src/schema/control-schema.sql
  • packages/local-server/src/schema/local-inserts.json
  • packages/local-server/src/schema/local-schema.sql
  • packages/local-server/src/scoped-delete.ts
  • packages/local-server/src/serve.ts
  • packages/local-server/src/service-map-rollup.ts
  • packages/local-server/src/store-claim.ts
  • packages/local-server/src/store-errors.ts
  • packages/local-server/src/store-version.ts
  • packages/local-server/src/ui-assets.ts
  • packages/local-server/src/ui-embed.gen.ts
  • packages/local-server/src/version.ts
  • packages/local-server/test/libchdb-provision.test.ts
  • packages/local-server/test/local-runtime.test.ts
  • packages/local-server/tsconfig.json
  • packages/query-engine/src/local.ts
  • scripts/build-local-binary.sh
  • scripts/bump-local-control-schema.ts
  • scripts/bump-local-schema.ts
  • scripts/check-local-schema-manifest.ts
  • scripts/gen-ui-embed.ts
  • scripts/generate-clickhouse-insert-mappings.ts
  • scripts/generate-clickhouse-schema-sql.ts
  • tsconfig.alchemy-maple.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant