Six fixes addressing operational correctness on a real Debian host:
1. Ownership after git operations: chown -R zopu:zopu on the checkout
after clone/update/rollback before running bun install/build as the
service user. .env kept at 0600 with explicit chmod after each
operation.
2. sudo replaced with runuser: minimal Debian does not include sudo.
runuser is part of util-linux (essential) and always available.
All three scripts (bootstrap, update, rollback) now use runuser -u.
3. cron.d entry fixed: /etc/cron.d format requires a username field.
Added ${SERVICE_USER} between the time fields and the command path.
4. Firewall: added explicit `ufw allow in on tailscale0` rule so the
deny-incoming default does not block Tailscale private-overlay
reachability. Removed inaccurate claim that direct private IP works
by default; documented that an explicit per-interface rule is needed.
5. SupplementaryGroups=docker added to zopu-agent.service: the Orb
sandbox runtime lives in the agent process, not just the daemon.
6. zopu-health.service: added Environment=ENV_FILE=__INSTALL_DIR__/.env
so health-check.sh sources the correct .env at custom install paths.
Verified Flue build output: bun run build:agents produces
packages/agents/dist/server.mjs (confirmed by running the build).
Agent unit ExecStart path is correct.
Validated: bash -n on all 6 shell scripts; mocked health-check smoke
against live RivetKit engine on port 6420; systemd unit structural
checks. No JS/TS/agent files changed.
code
This project was created with Better-T-Stack, a modern TypeScript stack that combines React, React Router, Convex, and more.
Features
- TypeScript - For type safety and improved developer experience
- React Router - Declarative routing for React
- React Native - Build mobile apps using React
- Expo - Tools for React Native development
- TailwindCSS - Utility-first CSS for rapid UI development
- Shared UI package - shadcn/ui primitives live in
packages/ui - Convex - Reactive backend-as-a-service platform
- Agent daemon - Bun/Effect service that leases Convex commands and dispatches them to AgentOS/RivetKit actors
- Authentication - Better-Auth
- Electrobun - Lightweight desktop shell for web frontends
- OpenTUI - Terminal interface in
apps/tui - Oxlint - Oxlint + Oxfmt (linting & formatting)
- Vite+ - Unified Vite toolchain, workspace task runner, linting, and formatting
Getting Started
First, install the dependencies:
bun install
Convex Setup
This project uses Convex as a backend. You'll need to set up Convex before running the app:
bun run dev:setup
Follow the prompts to create a new Convex project and connect it to your application.
Copy environment variables from packages/backend/.env.local to apps/*/.env.
Then, run the development server:
bun run dev
Open http://localhost:5173 in your browser to see the web application. Use the Expo Go app to run the mobile application. Your app will connect to the Convex cloud backend automatically.
Daemon setup
The daemon is a separate Bun process that connects to Convex, claims queued daemonCommands, and runs AgentOS actor actions. Copy apps/daemon/.env.example to apps/daemon/.env, replace the placeholder CONVEX_URL, then run:
bun run dev:daemon
Build the compiled daemon binary with bun run build:daemon; the output is apps/daemon/dist/code-daemon.
UI Customization
React web apps in this stack share shadcn/ui primitives through packages/ui.
- Change design tokens and global styles in
packages/ui/src/styles/globals.css - Update shared primitives in
packages/ui/src/components/* - Adjust shadcn aliases or style config in
packages/ui/components.jsonandapps/web/components.json
Add more shared components
Run this from the project root to add more primitives to the shared UI package:
npx shadcn@latest add accordion dialog popover sheet table -c packages/ui
Import shared components like this:
import { Button } from "@code/ui/components/button";
Add app-specific blocks
If you want to add app-specific blocks instead of shared primitives, run the shadcn CLI from apps/web.
Git Hooks and Formatting
- Optional native Vite+ hooks:
bun run hooks:setup - Docs: Vite+ commit hooks
- Run checks:
bun run check
Project Structure
code/
├── apps/
│ ├── web/ # Web application (React + React Router)
│ ├── native/ # Mobile application (React Native, Expo)
│ ├── desktop/ # Electrobun shell around the web build
│ ├── tui/ # OpenTUI terminal client
│ ├── daemon/ # Bun/Effect daemon for AgentOS command execution
├── packages/
│ ├── backend/ # Convex backend functions and schema
│ ├── primitives/ # Shared Effect services, including AgentOS helpers
│ ├── env/ # Runtime environment validation
│ ├── ui/ # Shared shadcn/ui components and styles
├── repos/ # Vendored upstream references; excluded from normal tooling
Available Scripts
bun run dev: Start all applications in development modebun run build: Build all applicationsbun run dev:web: Start only the web applicationbun run dev:server: Start the Convex development serverbun run dev:setup: Setup and configure your Convex projectbun run check-types: Check TypeScript types across all appsbun run dev:native: Start the React Native/Expo development serverbun run dev:tui: Start the terminal UIbun run check: Run Vite+ format/lint checks and workspace TypeScript checksbun run lint: Run Vite+ lint checksbun run format: Run Vite+ formattingbun run fix: Apply Ultracite fixesbun run staged: Run Vite+ checks against staged filesbun run hooks:setup: Install Vite+ native Git hooks withvp configbun run dev:desktop: Start the Electrobun desktop app with HMRbun run build:desktop: Build the stable Electrobun desktop appbun run build:desktop:canary: Build the canary Electrobun desktop appbun run dev:daemon: Start the daemon with Bun watch modebun run build:daemon: Compile the daemon binarybun run docs:update [commit-or-range]: Ask a detected coding-agent CLI to update first-party documentation for a commit or revision range; use--dry-runto inspect the promptbun run subtree list: List configured vendored Git subtreesbun run subtree status [name]: Inspect subtree metadata and local dirtinessbun run subtree preview <name>: Fetch and display upstream commits not yet represented by the subtreebun run subtree update <name> --yes: Safely pull a configured subtree with squash semantics