Show HN: HyCanvas, a free self-hostable Canva alternative HyCanvas, a free self-hostable AI-native design platform, has been released as an open-source alternative to Canva. The platform supports social graphics, presentations, videos, whiteboards, docs, and print with no paywalls or watermarks, and uses a Go backend with a Next.js frontend, PostgreSQL, and optional S3-compatible storage. HyCanvas is a free, self-hostable, AI-native design platform. Design anything - social graphics, presentations, videos, whiteboards, docs, and print - with no paywalls or watermarks. Web-only. The product spans a single-player editor, document types presentations, video, whiteboard, docs, sheets , export, brand kits, and a bring-your-own-key AI layer. Work not yet built is tracked under docs/roadmap/ . Open a template and make it yours - edit text, restyle, and export, all in the browser: Every design stays yours in an open format: download any document as a portable .hyc file readable JSON and import it on any instance, or export to PNG, PDF, and more, with no watermarks: The frontend and shared packages are an npm-workspaces monorepo orchestrated with concurrently and dotenv-cli against a single shared root .env ; the backend is a standalone Go module. backend - Go backend REST under /api/v1 , the /realtime WebSocket, the Go rendering engine for export, and DB migrations . Serves the exported frontend in the production bundle. Postgres only. frontend - Next.js app Pages Router , statically exported for production. packages/ - framework-agnostic @hc/ libraries schema, engine, editor, sdk, color, text, geometry, export, media, stock, templates, authz, formula, sheets, timeline, whiteboard, docs, publishing, website, print, a11y, ... . The frontend imports them from their built dist/ . scripts/build-dist.js - embeds the exported frontend into the Go binary go build -tags embed and writes the single self-contained dist/hycanvas . - illustrated user and operator guides: docs/ getting started /hyscaler/HyCanvas/blob/development/docs/getting-started.md , the dashboard /hyscaler/HyCanvas/blob/development/docs/dashboard.md , the editor /hyscaler/HyCanvas/blob/development/docs/editor.md , document types /hyscaler/HyCanvas/blob/development/docs/document-types.md , and the first-run setup wizard /hyscaler/HyCanvas/blob/development/docs/setup-wizard.md with every step screenshotted. docs/roadmap/ - forward-looking specs for work not yet built realtime collaboration, AI media, accessibility/i18n/enterprise . CLAUDE.md - working guidance for this repository. - Node 24 see .nvmrc for the frontend and shared packages. - Go 1.25 for the backend backend . - PostgreSQL. Object storage is optional S3-compatible / MinIO ; the backend falls back to local-file storage. - ffmpeg only if you want server-side video export already bundled in the Docker image . cp .env.example .env then edit values at minimum DATABASE URL + JWT SECRET npm install installs the frontend + shared packages npm run build:packages build the @hc/ libraries once the frontend imports their dist npm run db:migrate apply SQL migrations Go migrator npm run dev backend Go on :8005, frontend on :3000 Open http://localhost:3000 . The frontend reads the backend base URL from NEXT PUBLIC BACKEND URL defaults to http://localhost:8005/api . Notes for development: npm run build:packages is required before the first npm run dev and after editing any packages/ source , because the @hc/ packages are consumed from their compiled dist/ , not their source. npm run build and npm run build:dist build the packages for you.- The server also runs migrations on boot when DB AUTO MIGRATE=true , so npm run db:migrate is mainly for an explicit, pre-boot migration. - No SMTP is wired: verify-email / password-reset / magic-link links are read from the dev outbox in non-production instead of being emailed. The production bundle is a single self-contained binary: dist/hycanvas . The statically-exported frontend is baked into it go:embed , built with -tags embed , so one binary serves the frontend, the REST API, and the realtime WebSocket on one port, with no Node runtime and no sidecar public/ folder. It migrates the database on boot. The build also stamps the git version into the binary; it is logged on startup and returned by /healthz and /api/v1/ go/health . npm run build:dist builds @hc/ + the frontend routed to /api , embeds it, and compiles the Go binary into dist/ ./dist/hycanvas run it directly: the binary loads .env itself, no Node needed Running the bundle needs no Node at all: the Go binary loads a .env from the working directory or its parent on startup, so ./dist/hycanvas is fully standalone, exactly how it runs under Docker. Real environment variables always win over .env , so injected config containers, CI is never overridden. npm run start:dist:only is just a convenience alias for the same binary. If you build the binary without the embedded UI for example a plain npm run build:backend , set PUBLIC DIR to an exported frontend directory to serve it; with neither, the binary serves the API only and shows a short notice page. In the dist build the frontend talks to the same-origin /api no NEXT PUBLIC BACKEND URL needed , so the one process answers UI and API together. The binary can also run itself as a background service, so no external process manager is needed. ./dist/hycanvas start or just ./dist/hycanvas runs in the foreground; the service verbs manage a detached process with a pidfile and logfile next to the binary, on Linux, macOS, and Windows alike: ./dist/hycanvas service start detach into the background ./dist/hycanvas service status ./dist/hycanvas service log last log lines; -f follows ./dist/hycanvas service restart ./dist/hycanvas service stop The binary's directory is the service's working directory, which is where .env is read from. The service does not auto-start at boot; if you want that, add a crontab entry @reboot /path/to/hycanvas service start or use Docker. No .env yet? Just start the server. On an interactive terminal it first asks whether to set up in the browser or right there in the terminal: Web wizard default : the server boots into setup mode and prints a one-time wizard access secret; opening any page redirects to /installation/step-1 , which asks for that secret and then walks through PostgreSQL, storage local or S3 , and optional SMTP, testing each answer live with visible progress. Step 1 includes "Running HyCanvas behind a proxy?": when enabled you configure the external domain e.g. https://hycanvas.art separately from the internal host and port the proxy forwards to written as APP URL , BIND HOST , and PORT . Answers are held on the server never in the browser , and a page refresh always restarts at the welcome step. CLI wizard : the same questions asked in the terminal, with the same live validation and hidden password input. Either way the wizard writes .env secrets like JWT SECRET are generated automatically , runs the database migrations, starts the app in the same process, and in the browser flow creates your first account. Non-interactive starts Docker, pipes default to the web wizard. To skip all of it, create a .env by hand see .env.example before starting. npm run deploy rebuilds the bundle and restarts the running service in one step build:dist + ./dist/hycanvas service restart . When writing .env by hand for production, set at minimum NODE ENV=production , DATABASE URL , a strong JWT SECRET , APP URL public base URL used in generated links , and an absolute LOCAL STORAGE PATH or STORAGE DRIVER=s3 with S3 . AI provider keys are configured per workspace at runtime stored encrypted , never via env. HyCanvas serves plain HTTP; put nginx, Caddy, or Traefik in front for TLS. Three settings matter: APP URL is the external domain the proxy serves used in generated links and the OIDC redirect , PORT is the internal port the proxy forwards to, and BIND HOST=127.0.0.1 keeps the app reachable only through the proxy. The setup wizard configures all three when you answer "Running HyCanvas behind a proxy?" in step 1. The proxy must forward the Host header and for realtime collaboration WebSocket upgrades on /realtime . With an https APP URL , session cookies stay Secure automatically. Also raise the proxy's request-body limit: uploads travel as base64 JSON, about a third larger than the file on disk, and nginx's default client max body size of 1 MB rejects anything bigger with a 413 before HyCanvas ever sees it. Set it to comfortably above the largest upload you expect, for example client max body size 100m; . Caddy and Traefik impose no body limit by default. Social sign-in is configured via env and appears on the login and signup pages once set. For Google: - In Google Cloud Console https://console.cloud.google.com/apis/credentials , configure the OAuth consent screen, then create an OAuth client ID of type "Web application". - Add the authorized redirect URI: