Synaplan
开源 AI 软件平台,面向企业:自托管 AI 代理,符合 GDPR 的聊天机器人小部件,RAG 知识库,WhatsApp 与 Outlook AI,MCP,多模型路由。支持 Docker、Kubernetes 或在 Germ 托管。
AI 翻译
Epistola is an open-source product for document creation and automation.
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗[!NOTE] Epistola Suite 1.0.0 is generally available (31 July 2026). GA REST APIs, catalog formats, configuration, and architecture follow SemVer compatibility. Explicitly labelled alpha and beta features remain experimental and may change in a minor release. Review the changelog before updating and see the Release & Support Policy for supported versions and upgrades. Published security issues and their affected releases are listed in the vulnerability history.
A document suite application built with Spring Boot 4.0.0 and Kotlin, featuring server-side rendered views with Thymeleaf and HTMX.
epistola-suite/
├── apps/
│ └── epistola/ # Main Spring Boot application (Thymeleaf + HTMX)
├── modules/
│ └── editor/ # Rich text editor component (Vite + TypeScript)
├── build.gradle.kts # Root build configuration
└── settings.gradle.kts # Module includes
The frontend uses a server-side rendering approach:
Using Epistola? User documentation — guides and tutorials for authoring templates and running documents — is published at https://epistola.app/en/learn.
Working on Epistola? The technical documentation lives in docs/ and is indexed in
docs/README.md — every page grouped by topic, each labelled
with whether it describes shipped behavior, an experimental feature, a design
proposal, or a point-in-time record. It also links the
architecture decision records and the
catalog wire-format reference.
Quick jumps: Project overview · Document generation · Deployment · Authentication · Testing · REST/MCP surfaces
Install mise version manager
Run the init script to set up your development environment:
./scripts/init.sh
This will:
Development is actively tested on macOS and Linux. Windows is not tested; if you're on Windows, develop inside WSL2 (treated as Linux).
Your shell must use a UTF-8 locale (LANG/LC_ALL ending in .UTF-8). The
JVM derives sun.jnu.encoding — the charset for file paths — from the OS
locale, and a non-UTF-8 locale (the POSIX C locale is the default on many WSL
and minimal Linux images) breaks Kotlin builds when a source name contains a
non-ASCII character:
java.nio.file.InvalidPathException: Malformed input or input contains unmappable characters
./scripts/init.sh configures this automatically. To verify:
locale charmap # -> UTF-8
java -XshowSettings:properties -version 2>&1 | grep sun.jnu.encoding
# # -> sun.jnu.encoding = UTF-8
If you change the locale, run gradle --stop so the daemon restarts with it.
Tool versions (Java, Gradle, Node, pnpm and more) come from .mise.toml. The frontend builds
first: the editor bundle is a Gradle input, so gradle build needs it in place.
# First time, or after .mise.toml changes
mise install
# Frontend first — also runs the component-registry check
pnpm install
pnpm build
# Build the entire project
gradle build
# Run the application (requires a profile — see Authentication below)
gradle :apps:epistola:bootRun --args='--spring.profiles.active=local'
# Run it with demo data: a tenant, the demo catalog and a well-known API key
gradle :apps:epistola-demo:bootRun --args='--spring.profiles.active=demo,local,localauth'
Tests: gradle unitTest (no Docker), gradle integrationTest and gradle uiTest (both need
Docker), or gradle test for unit plus integration. Style: gradle ktlintFormat then
gradle ktlintCheck; pnpm format covers everything else.
For live frontend work, run the app with the local profile in one terminal and
pnpm --filter @epistola/editor watch in another — the watch rebuilds dist/ on change and the
local profile serves it from the filesystem.
Software bill of materials:
gradle :apps:epistola:generateSbom
pnpm --filter @epistola/editor run sbom # `run` matters: pnpm 11 has its own `sbom` command
While generating one, CycloneDX may print Unknown keyword meta:enum or Unknown keyword deprecated. Those are upstream SPDX schema-vocabulary notices from networknt, not Gradle
deprecations — leave the generated SBOM validation enabled.
A Spring profile must be set to configure authentication. Without one, the app will fail to start.
| Setup | Auth Method | Use Case |
|---|---|---|
local | Form login (in-memory users) | Local development |
local,keycloak | Form login + OAuth2/OIDC with local Keycloak | Testing both login methods |
localauth | Form login with env-var credentials | Staging/test fallback |
prod + OIDC env | OAuth2/OIDC with any compliant provider | Production |
Local development — uses in-memory users, no external dependencies:
gradle :apps:epistola:bootRun --args='--spring.profiles.active=local'
# Login: admin@local / admin or user@local / user
With local services — start PostgreSQL and Keycloak via the unified Docker Compose:
# Start PostgreSQL + Keycloak (admin console at http://localhost:4002, admin/admin)
docker compose -f apps/epistola/docker/docker-compose.yaml up -d
# Or start only PostgreSQL (sufficient for local profile)
docker compose -f apps/epistola/docker/docker-compose.yaml up -d postgres
# Run with form login and OAuth2
gradle :apps:epistola:bootRun --args='--spring.profiles.active=local,keycloak'
Production — configure OIDC via environment variables or the Helm chart's oidc.* values. Do not use the keycloak profile outside local development.
export SPRING_PROFILES_ACTIVE=prod
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KEYCLOAK_CLIENTID=epistola-suite
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KEYCLOAK_CLIENTSECRET=<your-secret>
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KEYCLOAK_SCOPE=openid,profile,email
export SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KEYCLOAK_ISSUERURI=https://sso.example.com/realms/epistola
export SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUERURI=https://sso.example.com/realms/epistola
See docs/auth.md for full details on OIDC setup, auto-provisioning, and safety guards.
Build a Docker image:
gradle :apps:epistola:bootBuildImage
Run the container:
docker run --rm -p 8080:8080 epistola:0.0.1-SNAPSHOT
The application can be deployed to Kubernetes using the Helm chart published to the OCI registry.
Install from the OCI registry:
helm install epistola oci://ghcr.io/epistola-app/charts/epistola --version <version>
Upgrade an existing release:
helm upgrade epistola oci://ghcr.io/epistola-app/charts/epistola --version <version>
Customize the deployment with values:
helm install epistola oci://ghcr.io/epistola-app/charts/epistola \
--version <version> \
--set image.tag=1.0.0 \
--set ingress.enabled=true \
--set ingress.hosts[0].host=epistola.example.com
See charts/epistola/values.yaml for all available configuration options.
The main Spring Boot application that serves the document suite. Uses Thymeleaf for server-side HTML rendering and HTMX for dynamic interactions.
A Vite-based TypeScript editor component that builds as a library and is embedded in the main application for rich text editing functionality. The built assets are served from /editor/.
To develop the editor standalone:
cd modules/editor
npm install
npm run dev
This project uses ktlint for Kotlin code style. Run the linter:
# Check code style
gradle ktlintCheck
# Auto-format code
gradle ktlintFormat
gradle test
Tests use Testcontainers which requires Docker to be running.
.mcp.json registers MCP servers for coding agents: GitHub (issues, pull requests, projects) and
the community forum. Each lives in .agents/mcp/ with a README covering its
one-time setup; no secrets are committed.
The project uses GitHub Actions for CI/CD:
latestpublish label: tagged with {version}-pr-{number}-{run}charts/ directorychart-X.Y.Z (separate versioning from the app)oci://ghcr.io/epistola-app/chartsVersion bumps are determined automatically from commit messages:
feat: - minor version bumpfix: - patch version bumpfeat!: or fix!: with BREAKING CHANGE - major version bumpBoth the app and Helm chart follow this convention. The Helm chart version is only bumped when changes are made to the charts/ directory.
Epistola Nederland B.V. publishes releases when ready and expects installations to stay on a supported version. Upgrades are designed to be direct (no intermediate releases required), with automated migrations and documented manual steps when needed. Downgrades after a successful upgrade are not supported; rollback of a failed upgrade is supported via backup restore.
Full details: SUPPORT_POLICY.md. Currently supported versions are published separately (website / release metadata), not hardcoded in the policy. See also docs/version-check.md and docs/migrations.md.
Shared with Epistola by default. A Suite installation shares its
installation id and version with Epistola Nederland B.V. by default (and, once
introduced, anonymous usage counters), so it can show whether the installation
is supported and see which versions are in use. No hostnames, tenant, user or
document data is ever shared. The daily release and security-advisory check
sends nothing that identifies the installation. Turn sharing off with
epistola.sharing.* (Helm: sharing), and the release check with
epistola.version-check.enabled=false; with everything off the installation
sends no data to Epistola. See
Data shared with Epistola.
Copyright in the first-party portions of Epistola Suite is held by Epistola Nederland B.V. Identified third-party materials and separately attributed contributions remain subject to their respective notices.
Epistola Suite is distributed under the GNU Affero General Public License v3. The software is provided "as is", without warranty of any kind. Epistola Nederland B.V. accepts no responsibility for the correctness of output or for any damages incurred through use of the software. Correctness assurances, defect remediation, support, and contractual liability coverage are available only under a separate commercial Service Level Agreement (SLA) with Epistola Nederland B.V. See DISCLAIMER.md for the full statement.