Environment Variables
Verto is configured entirely through environment variables. They select where
content comes from, whether the AI assistant is enabled, and (for the desktop
app) how GitHub sign-in works. The full list also lives in
.env.example at
the repo root.
The content source is resolved at build time — Verto is a statically rendered reader, so changing content or these variables means rebuilding. See Static Generation.
Content source
Pick the backend with VERTO_CONTENT_SOURCE; the rest depend on which source
you chose.
| Variable | Default | Notes |
|---|---|---|
VERTO_CONTENT_SOURCE | local | local | github | onedrive |
Local (default)
| Variable | Default | Notes |
|---|---|---|
VERTO_LOCAL_DIR | content | Folder to read .md / .mdx from; absolute or project-relative |
VERTO_CONTENT_SOURCE=local
VERTO_LOCAL_DIR=contentIn the desktop app you can instead pick a folder from the UI — see Local Folder.
GitHub
| Variable | Default | Notes |
|---|---|---|
VERTO_GITHUB_REPO | — (required) | owner/repo |
VERTO_GITHUB_BRANCH | main | Branch to read |
VERTO_GITHUB_PATH | content | Sub-directory in the repo |
VERTO_GITHUB_TOKEN | — (optional) | Required for private repos; raises the rate limit |
VERTO_CONTENT_SOURCE=github
VERTO_GITHUB_REPO=owner/repo
VERTO_GITHUB_BRANCH=main
VERTO_GITHUB_PATH=content
VERTO_GITHUB_TOKEN=ghp_xxxA single Git Trees API call enumerates the repo, then blobs are fetched on
demand. Without a token the unauthenticated limit is 60 requests/hour; a
fine-grained PAT with Contents: read raises it to 5000/hour.
OneDrive
Two modes. Share-URL mode is simplest — anyone with the link can read, no OAuth:
| Variable | Default | Notes |
|---|---|---|
VERTO_ONEDRIVE_SHARE_URL | — | Public share link to the folder |
VERTO_ONEDRIVE_PATH | — (optional) | Sub-folder inside the shared item |
VERTO_CONTENT_SOURCE=onedrive
VERTO_ONEDRIVE_SHARE_URL=https://1drv.ms/u/s!...
VERTO_ONEDRIVE_PATH=contentFor private content, register a Microsoft Entra (Azure AD) app with
Files.Read + offline_access, obtain a refresh token, then set:
| Variable | Default | Notes |
|---|---|---|
VERTO_ONEDRIVE_TENANT | common | common | consumers | a tenant GUID |
VERTO_ONEDRIVE_CLIENT_ID | — | Entra app client id |
VERTO_ONEDRIVE_CLIENT_SECRET | — | Entra app client secret |
VERTO_ONEDRIVE_REFRESH_TOKEN | — | Long-lived refresh token |
VERTO_ONEDRIVE_PATH | — (optional) | Sub-folder inside the drive |
VERTO_CONTENT_SOURCE=onedrive
VERTO_ONEDRIVE_TENANT=common
VERTO_ONEDRIVE_CLIENT_ID=...
VERTO_ONEDRIVE_CLIENT_SECRET=...
VERTO_ONEDRIVE_REFRESH_TOKEN=...
VERTO_ONEDRIVE_PATH=contentTokens refresh automatically each build; the source honors Graph
@odata.nextLink pagination and backs off on 429 / Retry-After.
Remote sources don't reliably surface a per-file modification time — prefer
frontmatter date / updated / order for deterministic sort. Also note that
navigation.json is read from the
source root, i.e. VERTO_GITHUB_PATH / VERTO_ONEDRIVE_PATH for remote
sources.
AI assistant
The "Ask AI" panel is off by default. Enable it by selecting a backend.
| Variable | Default | Notes |
|---|---|---|
NEXT_PUBLIC_VERTO_ASSISTANT | none | github (aliases: copilot, github-models) |
NEXT_PUBLIC_VERTO_ASSISTANT_MODEL | openai/gpt-4o-mini | Optional model override |
NEXT_PUBLIC_VERTO_ASSISTANT=github
NEXT_PUBLIC_VERTO_ASSISTANT_MODEL=openai/gpt-4o-miniTokens are never committed: the desktop build reuses the GitHub device-flow
sign-in token; the web build keeps a token you paste in localStorage only.
Desktop sign-in (GitHub OAuth Device Flow)
For desktop builds that sign in to GitHub at runtime, expose a public OAuth client id. There is no client secret in the device flow.
| Variable | Notes |
|---|---|
NEXT_PUBLIC_VERTO_GITHUB_CLIENT_ID | OAuth App client id, injected at build time |
VERTO_GITHUB_CLIENT_ID | GitHub Actions variable that release workflows map to the above |
# .env.local for local desktop source builds
NEXT_PUBLIC_VERTO_GITHUB_CLIENT_ID=Iv1.xxxxxxxxxxxxBecause it's a public value it belongs in an Actions Variable, not a Secret. If it's unset the build still succeeds, but the desktop Sign in button reports a missing client id.
Related
- Local Folder — choosing a folder from the desktop UI
- navigation.json — where the override file lives per source
- Static Generation — why content is resolved at build time