Files
edgeever/docs/self-hosting-architecture.md
T
tianma-if 3cc0b2d0ab fix(ai): restore browser fallback for model network failures
Relay initial browser network and CORS failures through authenticated, workspace-configured model endpoints while keeping native clients direct. Complete failed companion turns so the UI does not keep spinning.

Refs #619
2026-10-20 06:56:49 +08:00

5.7 KiB

Self-hosting and Docker architecture

Docker-based self-hosting supports VPS, NAS, and home servers without creating a second product implementation. Cloudflare and Docker execute the same Hono application and differ only at thin runtime and infrastructure adapter seams.

Current boundary

The API's business and route logic must depend on the storage contracts in apps/api/src/storage-contract.ts, not on a Cloudflare SDK type directly. The current concrete implementation lives in apps/api/src/cloudflare-storage-adapter.ts:

  • DatabaseAdapter: SQL statements and batches.
  • BlobStoreAdapter: attachment get, put, and delete operations.

Cloudflare injects the D1/R2 adapter into fetchEdgeEverApp; the Bun entrypoint injects the SQLite/filesystem or SQLite/S3 adapter into that same function. Neither entrypoint owns route or business decisions.

The shared self-hosted configuration shape is defined as SelfHostedStorageConfig, with one application data directory, one SQLite database file, and one attachment directory.

PostgreSQL is reserved as a second relational backend through the driver-neutral RelationalDatabaseAdapter and PostgreSQLStorageConfig contracts. It is intentionally not implemented yet. SQLite remains the default self-hosted database; PostgreSQL will be useful for larger teams, higher write concurrency, and external database operations.

Docker shape

The supported container deployment is a single application container with one persistent /data mount:

EdgeEver container
├── SQLite database          -> /data/edgeever.sqlite
├── credential secrets       -> /data/edgeever-secrets.json
└── attachment store         -> /data/resources

The self-hosted adapter preserves the existing SQLite schema and migrations/*.sql files. Attachments are addressed by the same opaque object keys stored in resources.object_key; the runtime supports both a local filesystem backend and an S3-compatible backend.

When PostgreSQL is implemented, it must introduce an explicit SQL dialect and migration set for PostgreSQL-specific full-text search and transaction behavior. It must not make the current SQLite/D1 migration files silently ambiguous.

Compatibility requirements

  • Keep /api/*, /mcp, /api/health, and the /api/openapi.json reachability probe unchanged. /api/openapi.json is not an API catalog.
  • Keep the current migration files append-only; do not fork the schema for Docker.
  • Keep root secrets in environment variables or Docker secrets, never in the image or database. The self-hosted runtime may persist a copy on /data outside SQLite so NAS/GUI upgrades that drop container environment variables keep credential encryption. Cloudflare continues to use Worker secrets only. Object-storage secrets and personal AI model API keys use separate purpose-specific keys derived from the existing instance authentication secret. An optional EDGE_EVER_CREDENTIALS_ENCRYPTION_KEY can override the AI credential key for advanced rotation. Stored credentials must remain AES-GCM ciphertext.
  • Make /data the only required persistent application path so NAS users can back up one volume.
  • Keep the production entry at scripts/self-hosted-server.js, and keep scripts/self-hosted-server.mjs as an alias for NAS/GUI command overrides saved from v1.62 and earlier.
  • Support EDGE_EVER_AUTH_USERNAME, EDGE_EVER_AUTH_PASSWORD, and session settings without Cloudflare-specific naming assumptions in the container entrypoint.
  • Keep login brute-force protection in the application layer with SQLite/D1- compatible storage; Cloudflare Rate Limiting and WAF may be optional deployment-level enhancements, but must not be required.
  • Expose a health check that distinguishes process availability from database readiness and attachment-store readiness.

Operational instructions, including secrets, HTTPS, backups, upgrades, and NAS permissions, live in Deploy EdgeEver with Docker.

The Docker image and local development use the same Bun runtime:

bun run build:web
EDGE_EVER_AUTH_PASSWORD='<strong-password>' bun run start:self-hosted

Set EDGE_EVER_DATA_DIR to the directory that should be persisted by Docker or a NAS volume. Long-running streaming responses use a 120-second idle timeout by default. Set EDGE_EVER_IDLE_TIMEOUT_SECONDS to a value from 10 to 255 to override it.

The same runtime can use an S3-compatible object store:

EDGE_EVER_STORAGE_BACKEND=s3 \
EDGE_EVER_S3_ENDPOINT='http://minio:9000' \
EDGE_EVER_S3_REGION='us-east-1' \
EDGE_EVER_S3_BUCKET='edgeever' \
EDGE_EVER_S3_ACCESS_KEY_ID='<access-key>' \
EDGE_EVER_S3_SECRET_ACCESS_KEY='<secret-key>' \
bun run start:self-hosted

The implementation uses @aws-sdk/client-s3 and does not load that SDK in the Cloudflare Worker entrypoint.

AI model requests

AI model requests run directly on the client by default, including note generation, conversations, tag suggestions, discoveries, infographics, connection tests, model listing, and extension video summaries. Web clients retry initial browser network/CORS failures through the authenticated instance relay. The relay only accepts model API endpoints belonging to enabled services configured in the current workspace, preserves streaming and cancellation, and never forwards browser cookies. HTTP errors and failures after a response has started do not trigger another model call. Desktop and native mobile clients continue to connect directly; private endpoints must be reachable from those devices. Browser fallback requires the instance to reach the model endpoint. Legacy generation endpoints still require a client update; the relay reuses client-side AI orchestration instead of duplicating it on the backend.