A content engine for Edge Delivery Services

Run your EDS project locally. Author it in place. Ship the same DOM.

ledge is one binary that serves an Edge Delivery Services project straight from disk — your blocks/, your scripts/aem.js, your styles/, unchanged — and opens the same page for editing on a second port. Content is rendered one way, model to DOM, on the server. There is no preview build to wait for.

Join the waitlist
How EDS compatibility works
Fig. 1
The authoring loop, before and after
flow
Edit
In the page, in the browser. Not a file, then a commit.
true
Render
Model to EDS DOM, on the server, on every request. Not a push, then a build.
true
Preview
The same page on the delivery port, already there.
false
Publish
One button. The preview store is copied to the published store, a page at a time.
false

Before, the first two steps ran through git and somebody else’s pipeline: edit a file, commit, push, wait for the preview to sync, reload, repeat. After, they are local and immediate — the editor writes the page model back, the server re-renders it, and the delivery port serves the result on the next request. Publishing is still a deliberate act, but it is a button, not a second pipeline.

Fig. 2
One binary, three ports, three contracts
ports
:3000 delivery
The pre-decoration EDS DOM your blocks already expect. Rendered with instrumentation off, so no page here carries a data-aue-* attribute.
false
:3001 authoring
The same markup plus data-aue-* instrumentation, and the WebAssembly editor that reads it.
true
:3002 published
A separate content store. HTML is cached for 60 seconds behind a content-hash ETag; image renditions are served as generated, never re-transformed.
false

One process, one page model, three jobs. Instrumentation is the only difference between the page markup :3001 renders and the page :3000 serves, and it is added at render time rather than stored — so a page cannot acquire editor attributes by being edited. :3002 reads its own store, which is why a draft on :3000 is not yet a page on :3002.

The editor

The same page, edited in place

Blocks are selected on the rendered page, not in a form beside it. The palette inserts and reorders them by drag; the inspector edits exactly the fields that block’s model declares.

The ledge editor: a block palette on the left, the rendered page in the centre with one block selected, and a properties inspector on the right.

Fig. 3 — the ledge editor on the authoring port: block palette in the left pane, properties and outline in the inspector. Placeholder until a screenshot replaces it; that swap is an authoring edit, not a deploy.

schematic-fallback

Getting started

One command, one config file

# serve an existing EDS project straight from disk
ledge ./my-eds-project

#   delivery   http://localhost:3000
#   authoring  http://localhost:3001
#   published  http://localhost:3002

Configuration is one file at the project root — the ports above are defaults, so this is only about the site itself and the extensions it uses:

ledge.toml[site]
origin = "https://example.com"
title = "Example"
description = "An Edge Delivery Services site, self-hosted."

[plugins]
enabled = true
dir = "plugins"

[forms]
dir = "forms"

[api]
public_page_export = true

[seed]
enabled = true

That is this site’s own ledge.toml, minus the domain. This page is an ordinary ledge page: seeded from JSON in the site’s repo on first boot, editable in the browser on the authoring port, and served to you from the delivery port. The syntax highlighting above is done by a WebAssembly plugin compiled from this same repo — there is no highlighter in the client bundle.

Capabilities

What ledge does

Five areas, each with a page of its own.

01
Local-first authoring

The two-panel editor, drag-to-insert blocks and sections, a page-revision journal with rollback, per-page publish, and live reload driven by a file watcher over SSE.

Local-first authoring
02
EDS compatibility

Your blocks/, styles/ and scripts/ unchanged, a byte-identical delivery DOM, and an importer that pulls an existing EDS site in - pages, assets, icons, and web fonts.

EDS compatibility
03
Extending ledge

Sandboxed WebAssembly plugins that render blocks and add authoring panels, minijinja block templates, and Ledge-native field types. The code sample above is rendered by a plugin.

Extending ledge
04
Content & data

Author sheets in a grid editor, server-rendered forms that write to a sheet, and generated query-index.json, sitemap.xml and RSS. The table below reads from a sheet.

Content & data
05
Self-hosting

A four-stage musl build into a scratch image with a healthcheck subcommand, rustls throughout with a test that fails on OpenSSL, and a second, smaller build profile.

Self-hosting

Build profiles

Two builds from one source tree

The full build is everything on this page. The EDS-only build compiles out every ledge-specific extension that carries a heavy dependency or an admin surface of its own, and keeps the whole of the EDS story: delivery, authoring, publishing, media, derived outputs.

Image

17.9 MB full · 9.89 MB EDS-only (musl, scratch)

Crates

477 full · 304 EDS-only (normal dependency graph)

Delivery

Byte-identical across both builds — one shared snapshot set, no per-profile variants

Ports

:3000 delivery · :3001 authoring · :3002 published

Capability Full EDS-only
Delivery, authoring & publish Yes Yes
WASM plugins Yes No
Block templates Yes No
Server-rendered forms Yes No
Authoring auth Yes No
Page history Yes No
Cache rules Yes No
Git code store Yes No
Metrics Yes No
Minification Yes No

Both figures are measured off ledge’s own dependency graph and image build, not estimated. A page a build cannot render is never rejected: a templated or plugin-claimed block falls through to the generic EDS block DOM, and a build that finds block templates it cannot render says so once at startup rather than silently serving different HTML. This table is a ledge sheet — changing a row is a grid edit at /_/sheets, not a deploy.

FAQ

Answers up front

Is this Adobe Edge Delivery Services, or something that looks like it?

It is a server for an EDS project. The blocks/, styles/ and scripts/ are the ones you already have — this site’s scripts/ is a byte-for-byte copy of the sample project’s, and a script in the repo fails if any file drifts. ledge renders the pre-decoration DOM those blocks expect and then gets out of the way. It is an independent project, not affiliated with Adobe.

What does “byte-identical delivery DOM” actually mean?

Three things. The delivery port and the published port emit the same bytes for the same page. The authoring port emits that same markup plus data-aue-* instrumentation and nothing else, so delivery renders with instrumentation off and carries none of it. And the EDS-only build is checked against the same render snapshots as the full build, so dropping features does not change the HTML.

Do I have to expose the authoring port in production?

No. All three ports bind, but only what your reverse proxy publishes is reachable — ledge terminates no TLS itself and expects a proxy in front. In the full build the authoring port carries real authentication: sessions, passkeys, roles, and a per-route capability guard. The EDS-only build has no auth compiled in at all, so it refuses to start if ledge.toml declares an [auth] section, and warns if the authoring port is bound to a non-loopback address.

Can I bring an existing EDS site over?

ledge import <root> --base <url> <path> fetches a page and follows its fragments recursively, writes the page and its assets into the preview store, harvests icons and web fonts on the way, and prints a report naming every block it could not model. Those are kept as passthrough content rather than dropped, so nothing disappears silently.

How finished is it?

Delivery, authoring, publishing, media renditions, sheets, forms, plugins, block templates, query-index, sitemaps, feeds and redirects are implemented and tested; this page alone exercises the plugin, block-template, sheet and form paths. What does not exist yet is a binary you can download, and there is no hosted service — ledge is self-hosted by design. The waitlist below is how you find out when the first half of that changes.

waitlist

Waitlist

One mail, when there is a binary.

No newsletter, no drip sequence, no passing your address to anyone. One message when ledge is downloadable, and that is the entire promise.