Andesine and the Vrite v2 rewrite on its default branch
Open-source adaptive content workspace
At a glance
- What is it?
- The v2 rewrite of Vrite is a TypeScript workspace for product docs and technical blogs, assembled by hand from a pnpm and turbo monorepo over Postgres, two Redis instances and Typesense. Its configuration files carry the interesting parts: a billing flag that stops three services from booting when it is missing, and an embedding dependency that keeps search off pure self-hosting.
- Who is it for?
- Andesine suits teams that want product documentation, a technical blog or a knowledge base on infrastructure they control, and who can accept operator duty for Postgres, two Redis instances, a Typesense index and an embedding provider. It does not suit teams wanting a finished v2, a one-command demo or an offline search stack.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 11 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 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Vrite ships as a v2 rewrite sitting on its own default branch
Vrite is an open-source, collaborative space for product documentation, technical blogs and knowledge bases. The repository at `vriteio/andesine` holds the v2 rewrite of that product, and the README opens by flagging this as a work in progress branch, with the latest stable Vrite kept on `main` instead. Since `andesine` is the default branch, a plain clone lands you on the rewrite rather than on the stable line.
What the product sets out to do is content production for technical teams. A management dashboard moves work through a Kanban or List view, the editor is WYSIWYG with Markdown support and an integrated code editor that handles formatting, and several people can work in the same document at once. Semantic search sits on top of that content base, and a documented API and extension system delivers the content to a frontend you choose. Hosting is your call: self-host, or sign up for Vrite Cloud at `app.vrite.io`.
Two details deserve attention before any of that. The README prints no install command for the self-hosted stack, so its only pointers are the usage guide at `docs.vrite.io` and the hosted trial. And the repository topics name `solidjs`, which fixes the front end stack without a single line of prose doing it.
A pnpm and turbo workspace split across apps and packages
The code is a monorepo rather than one deployable. The top level carries `pnpm-workspace.yaml`, `turbo.json`, `compose.yaml`, `.env.example` and `eslint.config.mjs`, with source split across `apps/` and `packages/`. The root manifest pins the toolchain: Node `>=24`, `[email protected]`, TypeScript `6.0.3` and turbo `2.10.8`.
Every developer entry point runs through `dotenv` and turbo, which explains the shape of the scripts:
"dev": "dotenv -- turbo dev",
"build": "dotenv -- turbo build",
"typecheck": "turbo run typecheck",
"db:migrate": "turbo run db:migrate --filter=@andesine/backend",
"db:studio": "turbo run db:studio --filter=@andesine/backend",Those are the commands to learn first. Migration and database inspection are filtered to `@andesine/backend`, so the database tooling lives inside the backend package and not at the root.
One filter is easy to misread. `openapi:export` runs `turbo run openapi:export --filter=@andesine/backend`, while `api:generate` runs `turbo run generate:api --filter=andesine` with no scope at all. One target is the scoped backend package; the other points at a package literally named `andesine` under `packages/`. Different names, different packages, and nothing in the manifest explains the split.
Postgres, two Redis instances and Typesense gate every boot
The stack behind the workspace is four stateful services. Postgres 17 alpine holds the data, with a `pg_isready` healthcheck that gates everything waiting on it. Typesense 30.2 keeps the search index and is probed on port 8108 through a curl healthcheck. Redis then appears twice: a default instance on 6379, plus a second one on 6380 built specifically for the job queue.
queue-redis:
image: redis:7-alpine
command:
- redis-server
- --appendonly
- "yes"
- --appendfsync
- everysec
- --maxmemory-policy
- noevictionAppend-only persistence with `everysec` fsync and a `noeviction` policy is a deliberate choice for a queue: queued work is not thrown away when memory fills up. The price is a second Redis to operate, with its own volume and its own port mapping, since jobs that vanish are worse than jobs that wait.
Migrations run as their own container rather than inside the backend. The `migrate` service builds from `apps/backend/Dockerfile`, runs `node ./dist/migrate.js`, waits for the database to report healthy, and only then lets the dependent services start. `.env.example` points `DATABASE_URL` at `postgresql://andesine:andesine@localhost:5432/andesine` for a host run, which is a different address from the service-to-service URL the compose file hands the migration container.
One billing flag can stop three services from starting
Three deployments have to agree on a single variable, and getting it wrong is fatal rather than merely degraded. Set `BILLING_ENABLED=true` or `BILLING_ENABLED=false` in the backend, the worker and the usage reporter; all three must carry the same value, and a missing or invalid value stops startup.
The change that forces this is stated without hedging: Stripe credentials no longer select the mode, and existing deployments without the variable will fail startup. No compatibility window is described, which makes this the first item to check on any upgrade.
With billing disabled, all Pro features remain available and the reporter skips usage reporting. That is the detail a skim misses. The flag governs payment plumbing, not feature access, so a self-hosted instance running with billing off still receives the paid feature set and simply sends nobody any numbers.
Billing enabled brings its own per-application list. The backend needs `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`, `STRIPE_PRO_SEAT_PRICE_ID`, `STRIPE_PRO_API_CALL_PRICE_ID` and `STRIPE_PRO_API_CALL_METER_EVENT_NAME`. The worker needs none of them. The reporter needs `STRIPE_SECRET_KEY` and the meter event name, always requires `DATABASE_URL`, and requires no backend host, authentication, email, Redis or search configuration. Its database, Stripe account and meter event name must match the backend, and `VERSION_RETENTION_DAYS` alongside `PRO_VERSION_RETENTION_DAYS` must match between backend and worker, because version cleanup and webhook recording both read them.
Version numbers and badge text disagree about the same project
The version numbers and the badge text in this repository do not agree with one another, and an adopter has to decide which to trust.
GitHub classifies the repository licence as Other, a custom licence it cannot categorise and defers to a file it does not quote, while the root manifest declares `AGPL-3.0-only` and the README badge alt text says Vrite is available under the AGPL-3.0 license. The two declared strings are not identical to each other, since one pins the only variant and the other does not, so read that file yourself before embedding the workspace in a closed product.
The version story carries its own mismatch. The root `package.json` reads `1.0.0`, while the tags on the repository stop at `v0.4.4` on 2024-07-05, followed by `v0.4.3` on 2024-06-11 and `v0.4.2` on 2024-05-12. Those tags belong to the previous line of Vrite, and nothing in the manifest ties them to this rewrite, so a version comparison between the two lines means nothing without checking the package name. Code, meanwhile, is arriving: the last push landed on 2026-10-04, which says more about the state of the branch than the tag list does.
Semantic search reaches out to an OpenAI-compatible endpoint
Semantic search is where the self-hosted stack stops being self-contained. Results come out of a Typesense index you control, but the embeddings behind them come from somewhere else entirely. `.env.example` lists `OPENAI_API_KEY` under required configuration, names `text-embedding-3-small` as `SEARCH_EMBEDDING_MODEL`, fixes `SEARCH_EMBEDDING_DIMENSIONS` at 1536, and offers `OPENAI_BASE_URL` as an optional override for another OpenAI-compatible API.
TYPESENSE_PORT=8108
TYPESENSE_URL=http://localhost:8108
# Use a unique secret outside local development.
TYPESENSE_API_KEY=typesense-dev-key
OPENAI_API_KEY=
SEARCH_EMBEDDING_MODEL=text-embedding-3-small
SEARCH_EMBEDDING_DIMENSIONS=1536The dimension value is not decoration. It is what the index is built around, so swapping in a model with a different output width means reconciling that number and the model name together, and reindexing is left to you. An operator unwilling to depend on an external embedding endpoint has no fully local configuration in this file.
Images follow a separate route. Entry images are analysed automatically, using `SEARCH_ASK_MODEL` when `ASSET_ANALYSIS_MODEL` is left unset, and the analysis model has to be vision capable with JSON output support. Meanwhile the Typesense key in the compose file defaults to `typesense-dev-key`, a value the example file explicitly tells you to replace outside local development.
Outbound webhooks refuse plain HTTP unless two switches agree
Webhook delivery has the sharpest default in the stack. Destinations default to public HTTPS, the exception list defaults to empty, and plain HTTP is refused unless two independent switches agree.
# HTTP needs BOTH the matching exception and WEBHOOK_ALLOW_HTTP=true.
WEBHOOK_ALLOW_HTTP=false
WEBHOOK_DESTINATION_EXCEPTIONS=[]Both settings must match between backend and worker, and exceptions use exact origins and IP addresses rather than patterns. The example file shows the local shape as a list of objects, each holding an `origin` and an `addresses` array. Either switch alone leaves HTTP blocked, which is the right default for a hosted service and turns local receiver testing into a three-step edit every time.
Concurrency splits across two queues. `WORKER_CONCURRENCY` sits at 4 for background jobs, while local webhook consumers run separately from search jobs with `WEBHOOK_WORKER_CONCURRENCY` at 4, `WEBHOOK_GLOBAL_CONCURRENCY` at 16 and `WEBHOOK_WORKSPACE_CONCURRENCY` at 4. The same global and workspace limits are meant to be applied on every worker replica, and each endpoint allows one active run, so a cap that lives per destination does not multiply when you add replicas.
Self-hosting against Vrite Cloud is a different operating model
Choosing Vrite Cloud over self-hosting is a real decision with a measurable difference. The hosted path at `app.vrite.io` means someone else runs Postgres, both Redis instances and the Typesense volume, and someone else holds the embedding credentials. Self-hosting puts four stateful services, three named volumes and the port mappings for 5432, 6380 and 8108 on your own hardware, with the migration container deciding when the rest may start. Documentation lives at `docs.vrite.io` either way.
A second alternative sits inside the project itself: the stable `main` branch. Choosing it means you get the shipped product and none of the v2 work, at the cost of the dashboard, the semantic search layer and the extension system that the rewrite is built around.
The rewrite is the wrong tool in a few clear situations. If you need a finished product now rather than a branch in progress, if your content cannot leave your network and you have no compatible embedding endpoint to point at, or if you cannot take on database migrations for a project whose upgrade path is not documented. Nothing here describes migrating an existing Vrite database onto this branch, and nothing describes rolling the rewrite back once it is running.
Editorial conclusion
Andesine suits teams that want product documentation, a technical blog or a knowledge base on infrastructure they control, and who can accept operator duty for Postgres, two Redis instances, a Typesense index and an embedding provider. It does not suit teams wanting a finished v2, a one-command demo or an offline search stack. Before adopting, check three things: that BILLING_ENABLED carries the same value on backend, worker and reporter, that ENCRYPTION_KEYS still lists every key needed to read stored secrets, and whether an existing Vrite database has any migration path, which the README does not document.
Frequently asked questions
What does it take to run vriteio/andesine locally?
Node `>=24` and `[email protected]` per the root manifest, plus the services declared in `compose.yaml`: Postgres, two Redis instances and Typesense 30.2. Required secrets come from `.env.example`, where `SECRET` is generated with `openssl rand -hex 32` and `ENCRYPTION_KEYS` holds a JSON list that backend and worker must share.
Can vriteio/andesine start without Stripe credentials?
Set `BILLING_ENABLED=false` on the backend, worker and usage reporter with the same value on all three. With billing disabled, all Pro features remain available and the reporter skips usage reporting, while a missing or invalid value stops startup.
Does vriteio/andesine search without an OpenAI key?
`OPENAI_API_KEY` appears under required configuration in `.env.example`, alongside `text-embedding-3-small` and `SEARCH_EMBEDDING_DIMENSIONS` set to 1536. The documented way to point elsewhere is the `OPENAI_BASE_URL` override for another OpenAI-compatible API.
Which branch should I use for Vrite, andesine or main?
The README marks `andesine` as a work in progress branch for the v2 rewrite and points to `main` for the latest stable version. Because `andesine` is the default branch of `vriteio/andesine`, a fresh clone starts on the rewrite.
How do I let Vrite webhooks reach a plain HTTP endpoint?
HTTP needs both a matching entry in `WEBHOOK_DESTINATION_EXCEPTIONS` naming the exact origin and IP address and `WEBHOOK_ALLOW_HTTP=true`. The defaults are an empty exception list and `false`, and both settings must match between backend and worker.
What licence is vriteio/andesine under?
GitHub classifies the repository licence as Other, a custom licence it cannot categorise and points to the LICENSE file, while the root `package.json` declares `AGPL-3.0-only` and the README badge alt text reads that Vrite is available under the AGPL-3.0 license. The two declared strings differ in whether they pin the only variant.
Official sources
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.
[](https://hysenlabs.com/projects/vriteio-andesine)Was this useful?