diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e288e25 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,15 @@ +node_modules +**/node_modules +.git +.gitignore +.gitattributes +.vscode +.prettierrc +investigation +vaults +demo-vaults +data +tmp +**/dist +packages/bridge-plugin/main.js +apps/ignis-server/server/plugins/*/plugin/main.js diff --git a/.gitignore b/.gitignore index f8eb38a..f1c2415 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ node_modules/ dist/ investigation/ vaults/ -plugin/main.js -server/plugins/*/plugin/main.js +packages/*/dist/ +packages/bridge-plugin/main.js +apps/ignis-server/server/plugins/*/plugin/main.js demo-vaults/ +data/ diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index afcf5d2..0000000 --- a/Dockerfile +++ /dev/null @@ -1,53 +0,0 @@ -# Build shim-loader.js -FROM node:22-slim AS build - -WORKDIR /build - -COPY package.json package-lock.json ./ -RUN npm ci --ignore-scripts - -COPY build.js ./ -COPY src/ ./src/ -COPY plugin/src/ ./plugin/src/ -COPY server/plugins/ ./server/plugins/ - -RUN npm run build - -# Production image. No Obsidian code included. -# On first run, the entrypoint downloads Obsidian. -FROM node:22-slim - -LABEL com.thiefling.ignis.obsidian-version="1.12.7" - -RUN apt-get update && apt-get install -y --no-install-recommends \ - ca-certificates curl gosu \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /app - -COPY package.json package-lock.json ./ -RUN npm ci --omit=dev --ignore-scripts - -COPY server/ ./server/ -COPY scripts/ ./scripts/ -COPY images/ ./images/ -COPY plugin/ ./plugin/ -COPY --from=build /build/dist ./dist -COPY --from=build /build/plugin/main.js ./plugin/main.js -COPY --from=build /build/server/plugins/headless-sync/plugin/main.js ./server/plugins/headless-sync/plugin/main.js - -RUN chmod +x /app/scripts/entrypoint.sh - -ENV PORT=8080 -ENV VAULT_ROOT=/vaults -ENV OBSIDIAN_VERSION=1.12.7 -ENV OBSIDIAN_ASSETS_PATH=/app/obsidian-app -ENV PUID=1000 -ENV PGID=1000 - -EXPOSE 8080 - -VOLUME /vaults -VOLUME /app/obsidian-app - -ENTRYPOINT ["/app/scripts/entrypoint.sh"] \ No newline at end of file diff --git a/README.md b/README.md index e4cb265..15e8628 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ What started as an experiment turned out to be more viable than expected, and th Plugin compatibility depends on what APIs a plugin uses; most plugins built on Obsidian's plugin API work, anything requiring Node native modules or `child_process` doesn't. See [What doesn't work](#what-doesnt-work) for the full list of known limitations. +## Variants + +Ignis ships currently ships as a self-hosted server but I have plans for a desktop plugin. The server variant code and Readme with details and setup instructions lives here: [`apps/ignis-server/`](apps/ignis-server/) + ## What works - All core editor features: markdown, canvas, bases, and the command palette. @@ -96,90 +100,6 @@ A few design decisions worth knowing about for someone evaluating Ignis against Tested in Chrome, Brave, and Firefox, with limited testing in Safari. -## Authentication - -Ignis has **no built-in authentication** and serves plain HTTP by default. Both authentication and TLS termination are expected to be handled by whatever you put in front of it. - -If you are exposing Ignis to the internet, **you should really** put an authentication layer in front of it. Options include: - -- A reverse proxy with Basic Auth (nginx, Caddy, Traefik) -- An SSO proxy like Authelia, Authentik, or OAuth2 Proxy -- A VPN (Tailscale, WireGuard) -- Cloudflare Application Tunnel - -Example for Basic Auth, and Authelia can be found [here](examples). - -> [!CAUTION] -> Do not run Ignis on a public network without auth. Anyone with the url can read and write your vault files. - - - -## Setup with Docker Compose - -Example `docker-compose.yml`: - -```yaml -services: - ignis: - image: nobbe/ignis:latest - ports: - - "8080:8080" - environment: - - OBSIDIAN_VERSION=1.12.7 - - PUID=1000 - - PGID=1000 - volumes: - - ./vaults:/vaults - - ./data:/app/data - - obsidian-app:/app/obsidian-app - restart: unless-stopped - -volumes: - obsidian-app: -``` - -Then `docker compose up -d`. On first start the container downloads Obsidian from the official source and installs Obsidian Headless CLI. This takes a minute or two. - -To build from source instead of pulling the image, clone the repo and replace `image: nobbe/ignis:latest` with `build: .`. - -### Volumes - -| Mount | Description | -| ----- | ----------- | -| `/vaults` | Vault storage. Each subdirectory is a vault. | -| `/data` | state persistence for various ignis specific functionality, plugin management, headless sync config, etc | -| `/app/obsidian-app` | Cached Obsidian assets. Persisting this avoids re-downloading on container recreate. | - -### Environment Variables - -| Variable | Description | Default | -| -------- | ----------- | ------- | -| `PORT` | Server listen port | `8080` | -| `VAULT_ROOT` | Path to vault storage inside the container | `/vaults` | -| `DATA_ROOT` | Path to persistent data (plugin config, sync state, auth tokens) | `/app/data` | -| `OBSIDIAN_VERSION` | Obsidian version to download | `1.12.7` | -| `OBSIDIAN_ASSETS_PATH` | Where the extracted Obsidian app files live. Override if you're pointing at a pre-extracted directory instead of letting the entrypoint download. | `/app/obsidian-app` | -| `AUTO_CREATE_DEFAULT` | When `true`, creates a "My Vault" vault on startup if no vaults exist. Useful for fresh installs. | `false` | -| `PUID` | User ID for file ownership | `1000` | -| `PGID` | Group ID for file ownership | `1000` | -| `WRITE_COALESCE_MS` | Debounce window (ms) for rapid writes. Useful for slow filesystems (rclone, NFS, SMB). Set to `0` to disable. | `5000` | - -Demo mode adds its own set of env vars (per-session vaults, auto-cleanup, proxy allowlist, login blocking). See [examples/demo/](examples/demo/) if you want to run a public demo deployment. - -### Migrating an existing vault - -Each subdirectory of `/vaults` is treated as a separate vault, so dropping in an existing Obsidian vault directory will make it available in Ignis. - -### Upgrading Obsidian - -Obsidian releases can include changes that break the compatibility shim. Each Ignis release pins a known-working Obsidian version through the `OBSIDIAN_VERSION` env var, so the recommended path is to wait for an Ignis release that bumps the version, pull the new image, and restart. - -If you want to try a newer Obsidian version before Ignis updates, set `OBSIDIAN_VERSION` in your compose file. The entrypoint will download that version on next start, but there's no guarantee it'll work cleanly with the current shim. - -### Backups - -Vault data lives as ordinary files in `/vaults`. Back it up however you back up other server-side data; Ignis doesn't provide a built in backup mechanism. - ## Contributing Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines, especially on how to report plugin compatibility issues. Check the [open issues](https://github.com/Nystik-gh/ignis/issues) for things to work on. @@ -198,4 +118,4 @@ Ignis is not affiliated with, endorsed by, or associated with Dynalist Inc. or O This work falls under the interoperability provisions of [Directive 2009/24/EC](https://eur-lex.europa.eu/eli/dir/2009/24/oj/eng) (the EU Software Directive), Article 6. See [LEGAL.md](LEGAL.md) for the full rationale. -This project exists because its author uses Obsidian daily and wants to access it from a browser. There is no intent to harm Obsidian, Dynalist Inc., or their business. If you are a representative of Dynalist Inc. and wish to discuss this project, please reach out: ignis@thiefling.com \ No newline at end of file +This project exists because its author uses Obsidian daily and wants to access it from a browser. There is no intent to harm Obsidian, Dynalist Inc., or their business. If you are a representative of Dynalist Inc. and wish to discuss this project, please reach out: ignis@thiefling.com diff --git a/apps/ignis-server/Dockerfile b/apps/ignis-server/Dockerfile new file mode 100644 index 0000000..95592fc --- /dev/null +++ b/apps/ignis-server/Dockerfile @@ -0,0 +1,77 @@ +# Build stage. +# Runs from the repo root as build context so workspace symlinks resolve. +# Copies workspace package.jsons first for cache-friendly npm ci. +FROM node:22-slim AS build + +WORKDIR /app + +COPY package.json package-lock.json ./ +COPY packages/services/package.json ./packages/services/ +COPY packages/shim/package.json ./packages/shim/ +COPY packages/ui/package.json ./packages/ui/ +COPY packages/bridge-plugin/package.json ./packages/bridge-plugin/ +COPY packages/server-core/package.json ./packages/server-core/ +COPY apps/ignis-server/package.json ./apps/ignis-server/ + +RUN npm ci --ignore-scripts + +COPY build.js ./ +COPY packages/ ./packages/ +COPY apps/ignis-server/ ./apps/ignis-server/ + +RUN npm run build + +# Production image. No Obsidian code included. +# On first run, the entrypoint downloads Obsidian. +FROM node:22-slim + +LABEL com.thiefling.ignis.obsidian-version="1.12.7" + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates curl gosu \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Workspace package.jsons + lockfile so npm ci sets up the @ignis/* symlinks. +COPY package.json package-lock.json ./ +COPY packages/services/package.json ./packages/services/ +COPY packages/shim/package.json ./packages/shim/ +COPY packages/ui/package.json ./packages/ui/ +COPY packages/bridge-plugin/package.json ./packages/bridge-plugin/ +COPY packages/server-core/package.json ./packages/server-core/ +COPY apps/ignis-server/package.json ./apps/ignis-server/ + +RUN npm ci --omit=dev --ignore-scripts + +# Runtime sources: the server, entrypoint script, repo-rooted images, and the server-core JS that gets required from the server at runtime. +COPY apps/ignis-server/server/ ./apps/ignis-server/server/ +COPY apps/ignis-server/scripts/ ./apps/ignis-server/scripts/ +COPY images/ ./images/ +COPY packages/server-core/src/ ./packages/server-core/src/ + +# Bridge plugin: manifest + styles for auto-install into vaults; built main.js comes from the build stage. +COPY packages/bridge-plugin/manifest.json ./packages/bridge-plugin/ +COPY packages/bridge-plugin/styles.css ./packages/bridge-plugin/ + +# Built artifacts from the build stage. +COPY --from=build /app/packages/shim/dist/shim-loader.js ./packages/shim/dist/shim-loader.js +COPY --from=build /app/packages/ui/dist/ignis-ui.js ./packages/ui/dist/ignis-ui.js +COPY --from=build /app/packages/bridge-plugin/main.js ./packages/bridge-plugin/main.js +COPY --from=build /app/apps/ignis-server/server/plugins/headless-sync/plugin/main.js ./apps/ignis-server/server/plugins/headless-sync/plugin/main.js + +RUN chmod +x /app/apps/ignis-server/scripts/entrypoint.sh + +ENV PORT=8080 +ENV VAULT_ROOT=/vaults +ENV OBSIDIAN_VERSION=1.12.7 +ENV OBSIDIAN_ASSETS_PATH=/app/obsidian-app +ENV PUID=1000 +ENV PGID=1000 + +EXPOSE 8080 + +VOLUME /vaults +VOLUME /app/obsidian-app + +ENTRYPOINT ["/app/apps/ignis-server/scripts/entrypoint.sh"] diff --git a/apps/ignis-server/README.md b/apps/ignis-server/README.md new file mode 100644 index 0000000..a5ff08c --- /dev/null +++ b/apps/ignis-server/README.md @@ -0,0 +1,95 @@ +# Ignis Server + +The self-hosted Docker variant of Ignis. For the project overview, feature list, and what works / what doesn't, see the [root README](../../README.md). + +## Contents + +- [Authentication](#authentication) +- [Setup with Docker Compose](#setup-with-docker-compose) +- [Volumes](#volumes) +- [Environment Variables](#environment-variables) +- [Migrating an existing vault](#migrating-an-existing-vault) +- [Upgrading Obsidian](#upgrading-obsidian) +- [Backups](#backups) + +## Authentication + +Ignis has **no built-in authentication** and serves plain HTTP by default. Both authentication and TLS termination are expected to be handled by whatever you put in front of it. + +If you are exposing Ignis to the internet, **you should really** put an authentication layer in front of it. Options include: + +- A reverse proxy with Basic Auth (nginx, Caddy, Traefik) +- An SSO proxy like Authelia, Authentik, or OAuth2 Proxy +- A VPN (Tailscale, WireGuard) +- Cloudflare Application Tunnel + +Example configurations for Basic Auth and Authelia are in [`examples/`](examples). + +> [!CAUTION] +> Do not run Ignis on a public network without auth. Anyone with the URL can read and write your vault files. + +## Setup with Docker Compose + +Example `docker-compose.yml`: + +```yaml +services: + ignis: + image: nobbe/ignis:latest + ports: + - "8080:8080" + environment: + - OBSIDIAN_VERSION=1.12.7 + - PUID=1000 + - PGID=1000 + volumes: + - ./vaults:/vaults + - ./data:/app/data + - obsidian-app:/app/obsidian-app + restart: unless-stopped + +volumes: + obsidian-app: +``` + +Then `docker compose up -d`. On first start the container downloads Obsidian from the official source and installs the Obsidian Headless CLI. This takes a minute or two. + +To build from source instead of pulling the image, clone the repo and run `docker compose up` against the [`docker-compose.yml`](docker-compose.yml) in this directory -- it is already wired to build from the monorepo root. + +## Volumes + +| Mount | Description | +| ----- | ----------- | +| `/vaults` | Vault storage. Each subdirectory is a vault. | +| `/app/data` | State persistence for various Ignis-specific functionality: plugin management, headless sync config, etc. | +| `/app/obsidian-app` | Cached Obsidian assets. Persisting this avoids re-downloading on container recreate. | + +## Environment Variables + +| Variable | Description | Default | +| -------- | ----------- | ------- | +| `PORT` | Server listen port | `8080` | +| `VAULT_ROOT` | Path to vault storage inside the container | `/vaults` | +| `DATA_ROOT` | Path to persistent data (plugin config, sync state, auth tokens) | `/app/data` | +| `OBSIDIAN_VERSION` | Obsidian version to download | `1.12.7` | +| `OBSIDIAN_ASSETS_PATH` | Where the extracted Obsidian app files live. Override if you're pointing at a pre-extracted directory instead of letting the entrypoint download. | `/app/obsidian-app` | +| `AUTO_CREATE_DEFAULT` | When `true`, creates a "My Vault" vault on startup if no vaults exist. Useful for fresh installs. | `false` | +| `PUID` | User ID for file ownership | `1000` | +| `PGID` | Group ID for file ownership | `1000` | +| `WRITE_COALESCE_MS` | Debounce window (ms) for rapid writes. Useful for slow filesystems (rclone, NFS, SMB). Set to `0` to disable. | `5000` | + +Demo mode adds its own set of env vars (per-session vaults, auto-cleanup, proxy allowlist, login blocking). See [`examples/demo/`](examples/demo/) if you want to run a public demo deployment. + +## Migrating an existing vault + +Each subdirectory of `/vaults` is treated as a separate vault, so dropping in an existing Obsidian vault directory will make it available in Ignis. + +## Upgrading Obsidian + +Obsidian releases can include changes that break the compatibility shim. Each Ignis release pins a known-working Obsidian version through the `OBSIDIAN_VERSION` env var, so the recommended path is to wait for an Ignis release that bumps the version, pull the new image, and restart. + +If you want to try a newer Obsidian version before Ignis updates, set `OBSIDIAN_VERSION` in your compose file. The entrypoint will download that version on next start, but there is no guarantee it will work cleanly with the current shim. + +## Backups + +Vault data lives as ordinary files in `/vaults`. Back it up however you back up other server-side data; Ignis does not provide a built-in backup mechanism. diff --git a/docker-compose.yml b/apps/ignis-server/docker-compose.yml similarity index 76% rename from docker-compose.yml rename to apps/ignis-server/docker-compose.yml index 4821d86..1c25985 100644 --- a/docker-compose.yml +++ b/apps/ignis-server/docker-compose.yml @@ -1,6 +1,8 @@ services: ignis: - build: . + build: + context: ../.. + dockerfile: apps/ignis-server/Dockerfile ports: - "8082:8080" environment: diff --git a/examples/INSTRUCTIONS.md b/apps/ignis-server/examples/INSTRUCTIONS.md similarity index 100% rename from examples/INSTRUCTIONS.md rename to apps/ignis-server/examples/INSTRUCTIONS.md diff --git a/examples/caddy-authelia/Caddyfile b/apps/ignis-server/examples/caddy-authelia/Caddyfile similarity index 100% rename from examples/caddy-authelia/Caddyfile rename to apps/ignis-server/examples/caddy-authelia/Caddyfile diff --git a/examples/caddy-authelia/authelia/configuration.yml b/apps/ignis-server/examples/caddy-authelia/authelia/configuration.yml similarity index 100% rename from examples/caddy-authelia/authelia/configuration.yml rename to apps/ignis-server/examples/caddy-authelia/authelia/configuration.yml diff --git a/examples/caddy-authelia/authelia/users_database.yml b/apps/ignis-server/examples/caddy-authelia/authelia/users_database.yml similarity index 100% rename from examples/caddy-authelia/authelia/users_database.yml rename to apps/ignis-server/examples/caddy-authelia/authelia/users_database.yml diff --git a/examples/caddy-authelia/docker-compose.yml b/apps/ignis-server/examples/caddy-authelia/docker-compose.yml similarity index 100% rename from examples/caddy-authelia/docker-compose.yml rename to apps/ignis-server/examples/caddy-authelia/docker-compose.yml diff --git a/examples/caddy-basic-auth/Caddyfile b/apps/ignis-server/examples/caddy-basic-auth/Caddyfile similarity index 100% rename from examples/caddy-basic-auth/Caddyfile rename to apps/ignis-server/examples/caddy-basic-auth/Caddyfile diff --git a/examples/caddy-basic-auth/docker-compose.yml b/apps/ignis-server/examples/caddy-basic-auth/docker-compose.yml similarity index 100% rename from examples/caddy-basic-auth/docker-compose.yml rename to apps/ignis-server/examples/caddy-basic-auth/docker-compose.yml diff --git a/examples/demo/README.md b/apps/ignis-server/examples/demo/README.md similarity index 100% rename from examples/demo/README.md rename to apps/ignis-server/examples/demo/README.md diff --git a/examples/demo/docker-compose.yml b/apps/ignis-server/examples/demo/docker-compose.yml similarity index 93% rename from examples/demo/docker-compose.yml rename to apps/ignis-server/examples/demo/docker-compose.yml index c08bf67..2e956cb 100644 --- a/examples/demo/docker-compose.yml +++ b/apps/ignis-server/examples/demo/docker-compose.yml @@ -9,7 +9,8 @@ services: ignis-demo: build: - context: ../.. + context: ../../../.. + dockerfile: apps/ignis-server/Dockerfile ports: - "8080:8080" environment: diff --git a/apps/ignis-server/package.json b/apps/ignis-server/package.json new file mode 100644 index 0000000..36ba745 --- /dev/null +++ b/apps/ignis-server/package.json @@ -0,0 +1,5 @@ +{ + "name": "@ignis/app", + "version": "0.0.0-internal", + "private": true +} diff --git a/scripts/entrypoint.sh b/apps/ignis-server/scripts/entrypoint.sh similarity index 97% rename from scripts/entrypoint.sh rename to apps/ignis-server/scripts/entrypoint.sh index e90e79c..3ba813d 100644 --- a/scripts/entrypoint.sh +++ b/apps/ignis-server/scripts/entrypoint.sh @@ -68,4 +68,4 @@ else fi # Run as the determined user -exec gosu "$RUN_USER" node /app/server/index.js \ No newline at end of file +exec gosu "$RUN_USER" node /app/apps/ignis-server/server/index.js \ No newline at end of file diff --git a/server/assets/github.svg b/apps/ignis-server/server/assets/github.svg similarity index 100% rename from server/assets/github.svg rename to apps/ignis-server/server/assets/github.svg diff --git a/server/assets/ignis.webp b/apps/ignis-server/server/assets/ignis.webp similarity index 100% rename from server/assets/ignis.webp rename to apps/ignis-server/server/assets/ignis.webp diff --git a/server/assets/index.html b/apps/ignis-server/server/assets/index.html similarity index 100% rename from server/assets/index.html rename to apps/ignis-server/server/assets/index.html index 36493ed..c3e7578 100644 --- a/server/assets/index.html +++ b/apps/ignis-server/server/assets/index.html @@ -41,8 +41,8 @@
Loading Obsidian...
- +