Open-source project
tantaman/strut avatar
tantaman/strut

Strut: spatial HTML5 presentation editor review

Strut - An Impress.js and Bespoke.js Presentation Editor

1,892 stars483 forksTypeScriptAGPL-3.0

At a glance

What is it?
Strut is a self-hostable TypeScript editor for arranging presentation slides in 3-D and presenting them as camera flights. Its Rindle data layer and same-origin API simplify local operation, but its AI features require a model key and its Cloudflare deployment requires a separately hosted daemon.
Who is it for?
Strut fits developers and technical presenters who want an editable, self-hosted spatial deck workflow and can operate its Rindle fleet and AI integration. It does not suit users expecting a zero-configuration hosted account, built-in inference, or a Workers deployment that includes the daemon.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 36 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Strut's spatial presentation editing problem

Strut targets people who build HTML5 presentations and want direct control over a deck's spatial arrangement rather than a linear sequence of full-screen slides. A deck contains rich content, slides occupy positions in a 3-D world, and presentation mode moves the camera through that world. The README describes this as the impress.js model with a visual editor attached to it. That distinction matters: Strut is an authoring interface for spatial decks, not a general-purpose office presentation converter or a hosted slide service.

The project is aimed at technical users comfortable with TypeScript, Vite, server routes, SQL migrations, and a local multi-process data fleet. It is a poor fit for someone who wants to sign up, upload a PowerPoint-like file, and present without managing local infrastructure. Strut also does not include a deployment-funded AI provider. Its AI routes depend on an OpenRouter key supplied by the user, and without that key the documented response is `402`.

The open repository is self-hostable and has no paywall in a clone. Optional hosted-storage, private-deck, and white-label behavior is connected through a `#commercial` seam, but the private official overlay is not part of the repository. That division makes Strut relevant to organizations that want control over hosting and data handling, while making the exact capabilities of the official hosted service different from what can be confirmed from the open code.

Rindle, TanStack Start, and the browser data path

Strut's architecture is centered on a Rindle fleet rather than a conventional standalone API process. The README names a `rindle-replicator` write-master, one `rindled` follower that owns the live-query WebSocket, and a `rindle-dev-edge` ingress. The ingress exposes control, read, and WebSocket traffic through one port. Its shape comes from `rindle.ncl`, while `rindle up` and `rindle dev` render and supervise it. Ports are allocated per checkout in `~/.rindle/ports.json` and written into `rindle.json`.

The application layer uses React 19, TanStack Start with file-based routing and SSR, and Vite. Server routes under `src/routes/api.rindle.*` host the stateless Rindle API through `server/rindle-api.ts`. These routes validate arguments, run authoritative SQL mutators, and register named queries. The same web process serves these endpoints and the image upload endpoint at `server/upload.ts`, so the normal local path does not require a separately started API server.

The browser code uses `@rindle/optimistic`, WASM, `useQuery`, and named `app.mutate.*` operations. It posts writes to `/api/rindle/*` and connects directly to the fleet ingress for live-query updates. SQL migrations in `migrations/` are the schema source of truth, while generated TypeScript, query definitions, and client mutators live in `shared/`. This is a meaningful operational commitment for self-hosters. It also gives the editor an explicit split between an optimistic client store and authoritative SQL operations on the server.

First local run with pnpm and Rindle

The documented normal local setup consists of dependency installation followed by the `dev` script. The `dev` script asks Rindle to render and supervise the fleet, apply migrations, generate `shared/schema.ts`, and keep watching `migrations/` while starting the web process.

Install dependencies with the following command. The repository identifies pnpm as its package manager and includes a `pnpm-lock.yaml`.

bash
pnpm install

After installation, start the normal development environment with:

bash
pnpm dev

The documentation says to open `http://localhost:3000` afterward. The web leg is `vite dev --port 3000`, while the Rindle CLI supplies connection bindings through environment variables. The README says the local upload fallback works without `.env`, so a first run can use the local `.uploads/` behavior. Copy `.env.example` to `.env` only when R2 upload storage or other optional server configuration is needed.

For a first real authoring check, open the editor and create or edit a slide, save the change, and start presentation mode. The documentation confirms slide authoring, spatial arrangement, and camera-flight playback, but it does not provide a sample deck or a documented step-by-step UI path. A new user should therefore treat the browser at port 3000 as the application entry point, not as evidence that any particular editor control has already been exercised.

Separating the web app from the Rindle daemon

Strut supports separate processes, which is useful when the fleet needs to outlive a web command. The documented scripts are:

bash
pnpm daemon
pnpm dev:web

`pnpm daemon` runs `rindle up --migrate --gen shared/schema.ts --watch`, while `pnpm dev:web` starts Vite on port 3000 and expects the fleet to be running. This split is not merely a convenience for terminal layout. It changes who owns the Rindle processes and how the application discovers the ingress.

The documented connection variables are specific. `RINDLE_URL` identifies the ingress for the normal Rindle execution path. Under `rindle exec`, the README also names `RINDLE_DAEMON_URL` and `RINDLE_FLEET_WS`. The server also resolves `RINDLE_DAEMON_URL` for the server or API side, `RINDLE_DAEMON_WS` for the URL handed to the browser, and `VITE_RINDLE_WS` as a build-time client fallback. `server/rindleEnv.ts` is the named configuration point. The README explicitly says there is no hardcoded localhost daemon default.

Production commands retain the same infrastructure boundary. `pnpm build` builds the client, SSR, and API routes but does not start the daemon. `pnpm preview` expects a reachable daemon. This makes deployment planning part of adoption: a successful web build is not equivalent to a complete deployment.

Cloudflare deployment and the separate daemon boundary

The documented Cloudflare path deploys the SSR application and `/api/rindle/*` routes to Cloudflare Workers, with image uploads handled through an R2 bucket binding. The repository provides `docs/DEPLOY_CLOUDFLARE.md` for daemon hosting, R2 setup, secrets, and deployment steps. The `deploy` script builds with `CF=1` and ships the Worker with Wrangler.

The key limitation is explicit: `rindled` cannot run on Workers and must be hosted separately. As a result, deploying Strut to Cloudflare is not equivalent to deploying every process in this repository into one runtime. Operators must arrange daemon hosting and the required connection bindings before expecting the browser, API routes, and live-query path to function. The README also distinguishes local `.uploads/` behavior from Cloudflare R2 storage.

Strut has two additional deployment boundaries. The optional commercial overlay is absent from a clone, while `pnpm deploy` builds the free application. The optional `deploy:pro` script expects a git-ignored `commercial/` overlay and applies `strut-auth` D1 migrations before building the combined Worker. That is a separate commercial deployment path, not a default feature of the open repository.

The `.env.example` also describes runnable artifacts as untrusted author code in a sandboxed iframe with an opaque origin. It recommends serving built artifacts from a separate origin for cross-origin isolation and HTTP CSP headers. The README excerpt does not provide the complete configuration or claim that the default local setup already satisfies that deployment recommendation.

AI features, licensing, and maintenance limits

Strut's AI feature routes depend on a model supplied by the user through OpenRouter. The documentation states that connecting an OpenRouter key enables AI across the editor and that usage is charged to that key. Without a key, AI routes answer `402` and the editor presents a connect-a-model gate. Strut does not include deployment-funded provider keys, an AI quota ledger, a generated-image backend, or speech-to-text backend. This makes the cost model explicit, but it also means an adoption decision must include whether users can provide and manage their own inference access.

The repository license is AGPL-3.0. That identifier governs use and redistribution of the open project, including obligations and conditions that depend on the distribution context. The repository description alone is not enough to determine whether a particular deployment, modification, or service arrangement satisfies the license, so the LICENSE file and the relevant terms need review before redistribution or network deployment.

The repository is not archived, and its last push was on 2026-09-17. That supports describing the project as recently pushed, but it does not establish a release cadence. GitHub releases are not listed, and the repository has no GitHub releases. Upgrade planning therefore cannot rely on a documented release history in this repository state. The practical cost of upgrades is associated with the code stack and data shape: React, TanStack Start, Vite, Rindle, generated schema files, migrations, daemon deployment, and Worker configuration.

Editorial conclusion

Strut fits developers and technical presenters who want an editable, self-hosted spatial deck workflow and can operate its Rindle fleet and AI integration. It does not suit users expecting a zero-configuration hosted account, built-in inference, or a Workers deployment that includes the daemon. Evaluate Strut first by confirming that your presentation model fits the impress.js camera-flight approach, then verify the local port allocation, image storage choice, and separately hosted daemon required for production.

Frequently asked questions

How do I install and run Strut locally?

Run `pnpm install` and then `pnpm dev`, after which the README says to open `http://localhost:3000`. The command starts the Rindle development fleet and the Vite application together.

Does Strut need a separate API server for local development?

No separate API server is documented for the normal local path. TanStack Start server routes under `/api/rindle/*` are served by the same Vite web process, although the Rindle fleet still contains the replicator, follower, and ingress processes.

Does Strut include AI access by default?

No. Its AI features use a model supplied by the user through an OpenRouter key, and the documentation states that AI routes return `402` when no key is connected.

Can the Strut daemon run on Cloudflare Workers?

No. The README explicitly says that `rindled` cannot run on Workers and must be hosted separately, even though the web app, SSR, and API routes can deploy to Workers.

Is the commercial Strut overlay included in the repository?

No. The README describes the official hosted marketing and paid Pro overlay as a private overlay merged at deployment time. A clone contains the open-core `#commercial` seam, but not the private overlay itself.

Official sources

  1. Issues
  2. License: AGPL-3.0
  3. Project website
  4. README
  5. tantaman/strut on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tantaman-strut.svg)](https://hysenlabs.com/projects/tantaman-strut)