open-design/flake.nix
Chris Tam c61ba320fd
feat(nix): Add official flake with home-manager and NixOS support (#402)
* nix: add official flake with home-manager and nixos modules

* Pin pnpm version

* Format README.md

* Populate PATH files to discover installed CLIs

* Revert "Populate PATH files to discover installed CLIs"

This reverts commit 18d88781a88b8781913cf5a8b680dfb38eabf7e4.

* Fix missing sqlite issue

* Fix system issue

* Reapply "Populate PATH files to discover installed CLIs"

This reverts commit d02ea994e6.

* Handle different ports for web frontend

* Provide documentation for getting pnpm hash

* Enable nix flake checks for code changes

* Set `OD_WEB_PORT` on daemon when declared

* fix: Fix environmentFile for macOS targets

* chore: Ignore nix and direnv related files

* fix: Read version directly from `package.json`

* feat: Make nix shell entry prettier

* chore: Update pnpm hashes

* chore: Bump `pnpm` hashes

* docs: Add blurb about dev shell in `README.md`

* Address review comments

* Add support for `OD_WEB_ORIGINS`

* Fix `isLocalSameOrigin`

* Update pnpm checksums

* docs: Update documentation on host origins

* Move allowedOrigins mapping out of the webFrontend.enable guard

* fix: Bump pnpm hashes

* Remove changes to `daemon` with `main` changes

`main` merged a feature that addressed our need for allowed origins.
Since this feature branch no longer needs it, remove any remaining
changes in `daemon` code so that this is a pure Nix change.

* Update documentation around `OD_DAEMON_URL`

* Rewrite option docs to match same-origin proxy contract

The port, webFrontend, and webFrontend.port option descriptions still
described OD_DAEMON_URL as the runtime contract for the SPA, but the
SPA issues relative /api/*, /artifacts/*, /frames/* requests and there
is no runtime daemon-URL injection. Rewrite the three blocks to
describe what the caddy / custom proxy must actually do.

* Document daemon-side requirements for custom-server proxy paths

The bring-your-own-server path in section (3) and the same-origin
contract in section (4) understated what the daemon needs: any proxy
whose origin differs from the daemon's bind (including loopback
split-port like 127.0.0.1:8080 while the daemon stays on :7457) is
403'd by the daemon's same-origin gate until told about that origin.

Add a callout under section (3)'s table, expand section (4) with a
decision table covering same-port, loopback split-port (OD_WEB_PORT or
webFrontend.allowedOrigins), and non-loopback (webFrontend.allowedOrigins)
cases, and rewrite the webFrontend.allowedOrigins option description to
enumerate the cases where it's required and surface OD_WEB_PORT as an
alternative for the loopback split-port case.

---------

Co-authored-by: lefarcen <935902669@qq.com>
2026-05-09 23:50:16 +08:00

120 lines
3.5 KiB
Nix

{
description = "Open Design local-first design product. Daemon (`od` CLI) + Next.js static web frontend.";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
dream2nix = {
url = "github:nix-community/dream2nix";
inputs.nixpkgs.follows = "nixpkgs";
};
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = {
self,
nixpkgs,
flake-utils,
dream2nix,
home-manager,
}: let
perSystem = flake-utils.lib.eachDefaultSystem (system: let
pkgs = import nixpkgs {inherit system;};
nodejs = pkgs.nodejs_24;
# nixpkgs ships pnpm 10.33.0; the repo's package.json declares
# `engines.pnpm: ">=10.33.2 <11"` and pnpm refuses to install
# against an older binary. Override the upstream tarball to
# the exact version pinned by `packageManager`. Bump the url
# + hash in lockstep with package.json#packageManager.
#
# When bumping versions, run the following to get a new hash:
#
# ```bash
# nix store prefetch-file --hash-type sha256 \
# https://registry.npmjs.org/pnpm/-/pnpm-${NEW_VERSION}.tgz
# ```
pnpm_10 = pkgs.pnpm_10.overrideAttrs (_old: rec {
version = "10.33.2";
src = pkgs.fetchurl {
url = "https://registry.npmjs.org/pnpm/-/pnpm-${version}.tgz";
hash = "sha256-envPE9f2zrOUbAOXg3PZm+n94cr8MAC9/tTE95EWdhA=";
};
});
daemon = pkgs.callPackage ./nix/package-daemon.nix {
inherit dream2nix nixpkgs system nodejs pnpm_10;
src = self;
};
web = pkgs.callPackage ./nix/package-web.nix {
inherit dream2nix nixpkgs system nodejs pnpm_10;
src = self;
};
in {
packages = {
inherit daemon web;
default = daemon;
};
# Wrap `od` with `--no-open` for `nix run`: the daemon package
# builds the daemon workspace only, not `apps/web/out/`, so the
# browser would otherwise auto-open onto an empty static dir.
apps.default = {
type = "app";
program = "${pkgs.writeShellScript "od-nix-run" ''
exec ${daemon}/bin/od --no-open "$@"
''}";
meta.description = "Open Design local daemon (`od`)";
};
devShells.default = pkgs.mkShell {
packages = [
nodejs
pnpm_10
];
shellHook = ''
echo "🎨 Open Design dev shell loaded!"
echo ""
echo "Language runtimes:"
echo " - 🐢 Node.js: $(node --version 2>/dev/null || echo 'not found')"
echo " - 📦 pnpm: $(pnpm --version 2>/dev/null || echo 'not found')"
echo ""
echo "Quick start:"
echo " - 🚀 pnpm install"
echo " - 🚀 pnpm tools-dev # local lifecycle entry point"
echo ""
'';
};
checks = {
daemon = daemon;
web = web;
};
formatter = pkgs.nixpkgs-fmt;
});
moduleCommon = import ./nix/module-common.nix;
in
perSystem
// {
homeManagerModules = rec {
open-design = import ./nix/home-manager.nix {
inherit moduleCommon;
flake = self;
};
default = open-design;
};
nixosModules = rec {
open-design = import ./nix/nixos.nix {
inherit moduleCommon;
flake = self;
};
default = open-design;
};
};
}