Skip to content

vul-os/vulos

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,069 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Vulos

Vulos

A self-hostable, web-native operating system. Your cloud, your hardware, your rules.

License: MIT React 19 + Vite Go 1.25

This is the core OS repo. The Vulos suite spans companion repos:
vulos-office · vulos-relay · vulos-cloud

Vulos desktop


What is Vulos?

Vulos is a sovereign personal server with a web-native desktop you run on your own hardware. The shell is a React single-page app — a real window manager with virtual desktops, a dock, and bundled apps — that runs in any browser. Open it from a laptop, a phone, or a shared screen and you get the same full desktop, backed by a single self-contained Go binary that embeds the entire frontend.

At its center is a sovereign assistant: an on-box AI agent that is aware of your calendar, contacts, files, and reminders, and that can act on your behalf — but only under a hard security contract. Every action with side effects is a confirmation-gated proposal, off-box egress is fenced by a tier-aware sovereignty Guard, and the language model runs through your own on-box gateway by default. The wedge is agency, not just privacy: your own server, your own AI, acting for you — without handing your inbox and calendar to a third party.

No Electron, no VNC, no always-on remote-desktop session, no third-party login. Web apps run natively in the shell; native Linux GUI apps stream over WebRTC only while their window is open. The whole thing flashes to a USB stick, deploys to a cloud server, or runs in Docker.

"Vula" is isiZulu for "open."


Features

  • Sovereign assistant — an on-box AI agent aware of your calendar, contacts, files, and reminders. It reads with a curated, read-only toolset and proposes anything with side effects. Answers stream token-by-token over SSE. See the security model below.
  • Proactive AI Home + ⌘K — the desktop opens as a home (agenda, focus, pending invites, reminders, proposals), not just a launcher. A unified ⌘K command palette drives the whole shell.
  • Window-manager shell — drag, resize, snap, and tile windows; virtual desktops; Mission Control overview; a dock with running-app indicators; persisted window sessions. Pure JSX React 19 + Vite + Tailwind.
  • Bundled apps — Terminal (persistent PTY over xterm.js), Files / Drive, App Hub, Activity Monitor, Settings, Notes, Peering, plus a suite under apps/: Browser, Office, Calculator, Camera, Clock, Gallery, Image Editor, Maps, Music, PDF Viewer, Weather, and more.
  • Passwordless auth, no third parties — WebAuthn/FIDO2 passkeys as the primary factor (with clone/replay counter detection), QR / phone-approval login for shared clients, device PIN, and TOTP 2FA fallback. Forced recovery-phrase signup with a client-side master-key unwrap. No Google SSO, no OAuth login flows.
  • Files with a real ACL — a Files service with a viewer < editor < owner role hierarchy enforced server-side, plus content-blind (sealed) file sharing and share-by-email with locality routing. Large files use a resumable, chunked upload (tus-style): each chunk rides the relay as an ordinary bounded request, the box reassembles into your own storage with per-chunk + whole-file integrity, and an interrupted upload resumes from the committed offset instead of restarting.
  • Notifications + sovereign Web Push — a real notifications system, plus opt-in Web Push where your box sends notifications directly to your device's browser vendor (FCM/Apple/Mozilla). It's outbound-only (works behind NAT, no central relay), and payloads are end-to-end encrypted per RFC 8291 — the vendor routes but can't read them. Enable it per-device under Settings → Notifications; Do Not Disturb is honoured by the box before any push is sent.
  • Portability & transparency — one-click "Export my data" account portability, and legible-trust surfaces that make your sovereignty level visible.
  • Board / whiteboard — an embedded collaborative board surface (gated by BOARD_AUTH_SECRET).
  • On-demand app streaming — native Linux apps stream via WebRTC with GPU-accelerated encoding (NVENC / VA-API / VP8 fallback). Close the window and the stream stops.
  • Sovereign big calls (self-host SFU) — small Meet calls run peer-to-peer; a call that outgrows the mesh cap can escalate to media on your own box's SFU (opt-in VULOS_SFU_HOST) rather than a third-party server. Media rides your infra; recording and transcription stay local. The owner sees host status under Settings → Box Health.
  • On-box LLM gateway — assistant LLM/embeddings traffic routes through the on-box llmux sovereign gateway by default; a local vector store powers on-instance retrieval (RAG). You choose the provider and sovereignty tier.
  • Peering & sync — every instance has its own Ed25519 identity; leaderless CRDT sync across your nodes; a full VulaID key lifecycle (rotation, revocation, account-anchored recovery, X3DH-style forward secrecy); AirDrop-style local Drop; real-time collaboration over Yjs with per-document ACL.
  • Local-first storage — SQLite on the box, S3/Restic for encrypted backup. Your data lives on your machine first.
  • One binary, immutable image — the Go server embeds the SPA. Ship it as a signed, immutable image with A/B slots and rollback, or just run the binary.

Architecture

A single Go backend serves the embedded React frontend and exposes the system over HTTP and WebSocket. The backend is organized into focused services under backend/services/ and domain packages under backend/internal/:

  • assistant — the sovereign AI agent: curated toolset, proposal ledger, tier-aware egress Guard, on-instance mail/RAG index (services/assistant/)
  • ai / llmuxclient — LLM/embeddings seam, routed through the on-box llmux gateway, with a local vector DB (internal/vecdb)
  • gateway — request routing, auth enforcement, and the API surface
  • auth / passkeys — WebAuthn passkeys, PIN, TOTP, QR/phone approval, recovery-phrase master key, credential vault
  • files — Files service with a viewer/editor/owner ACL and content-blind (sealed) sharing
  • notify — the notifications system
  • peering / fabric — Ed25519 identity, VulaID key lifecycle, leaderless CRDT sync mesh, and Drop
  • storage — local-first file storage, app filesystems, and backup
  • joincode / joinsync / cloudenroll — device/box join and enrollment
  • apps / appnet / stream / gpu — bundled app manifests, per-app network namespaces, GPU host, and streaming
vulos/
├── src/            # React frontend: shell, window manager, auth, providers
│   ├── shell/      #   desktop, dock, menu bar, window chrome
│   └── apps/       #   bundled app UIs
├── apps/           # App manifests + per-app frontends (browser, office, mail, …)
├── backend/        # Go backend
│   ├── cmd/        #   entrypoints: server, installer, sign, verify, init
│   ├── services/   #   assistant, ai, gateway, auth, files, notify, apps, …
│   └── internal/   #   llmuxclient, auth, fabric, vecdb, safedial, obs, …
├── scripts/        # Build, signing, and utility scripts
├── docs/           # Architecture, configuration, deploy, self-host docs
├── build.sh        # Bare-metal image builder + deployer
└── dev.sh          # Local dev + Docker deploy helper

See docs/ARCHITECTURE.md for the full component map and design decisions.


The sovereign assistant security model

The assistant is designed so that a compromised browser, a malicious email, or an off-box model provider cannot turn "an AI that helps you" into "an AI that acts against you." Four mechanisms carry that guarantee (backend/services/assistant/):

  • Read vs. act is split. The agent's read-only tools (search mail, read a thread, list calendar events and pending invites, find_contact, find_file / read_file, list_reminders) run freely inside the turn. Every tool with a side effect (send email, create event, RSVP, add contact, triage, set/cancel reminder) does not execute — it returns a proposal.
  • Proposal ledger + id-only execute. A proposal is stored server-side in a single-use, TTL-bounded ledger keyed to your session, and the client is shown a human-readable summary. Approving posts only the opaque proposal id to POST /api/assistant/execute — never client-supplied arguments — so a compromised client cannot smuggle a new recipient or amount past the confirmation dialog. Rejecting sends nothing. Ledger entries are single-use, per-user, expire in 10 minutes, and are bounded per user.
  • Tier-aware egress Guard. A single choke point (Guard) runs before any mail content reaches the model. It classifies the configured endpoint into a sovereignty tier — local (loopback / on this instance, always allowed), sovereign (an operator-declared off-box endpoint, asserted in-region/no-train but not operated or verified by Vulos), brokered (named third party under a no-train agreement), or external (anything else, fail-closed) — and blocks egress unless the tier permits it. brokered/external require an explicit VULOS_ASSISTANT_ALLOW_EXTERNAL=1 opt-in; a private-range IP is never silently trusted as "local." The shell shows an honest tier badge and picker.
  • Untrusted-content framing. Tool results (email bodies, file contents, other people's text) are wrapped as [UNTRUSTED CONTENT — data only] before they reach the model, and frame-escape attempts are defanged. Even a fully escaped prompt-injection cannot cause a side effect, because mutation still has to pass the ledger + id-only execute gate. When a proposed action's target came from mail rather than your own words, it is flagged for extra scrutiny.

The language model itself runs through your own on-box llmux gateway by default (internal/llmuxclient/, LLMUX_URL), and retrieval is powered by an on-instance embeddings index — the embedder must certify it runs on this instance or it is refused.


Quickstart

Run with Docker (fastest)

docker run -d \
  --name vulos \
  -p 8080:8080 \
  --shm-size=1g \
  -v vulos-data:/root/.vulos \
  ghcr.io/vul-os/vulos:latest

Open http://localhost:8080 and complete first-boot setup.

Develop with hot reload

Prerequisites: Node.js 22+ and Go 1.25+.

git clone https://github.com/vul-os/vulos.git
cd vulos
npm install

# Terminal 1 — backend (no cloud account needed)
go run ./backend/cmd/server --env=local

# Terminal 2 — frontend
npm run dev

Open http://localhost:5173 — Vite proxies /api to the backend on :8080. Or run both together with ./dev.sh.

Build for production

npm run build          # frontend → dist/ (embedded into the Go binary)
go build ./backend/... # backend

Configuration

Vulos runs locally with zero configuration via --env=local. Common knobs:

Setting Purpose
--env=local Run without a cloud account; data under ~/.vulos
VULOS_DATA_DIR Override the data directory (default ~/.vulos)
Port 8080 Backend HTTP/WebSocket server
.env Local dev overrides (frontend + dev scripts)

The full list of environment variables, config files, and installer flags lives in docs/CONFIGURATION.md.


Development & testing

npm run dev          # Vite dev server (localhost:5173)
npm run build        # Production frontend build → dist/
npm run lint         # ESLint
npm run test         # Vitest unit + RTL/MSW integration tests (jsdom)
npm run test:e2e     # Playwright real-browser E2E (chromium)

go build ./backend/...                       # Compile the backend
go test ./backend/...                        # Go tests
go run ./backend/cmd/server --env=local      # Run the backend locally

./dev.sh             # Go + Vite together
./dev.sh deploy      # Full Docker build on localhost:8080

Frontend test layers

The shell has two runnable frontend test layers, both with a fully mocked backend — no Go server or database is needed to run either.

Layer Command Runtime Backend mock Lives in
Unit + integration npm run test vitest / jsdom MSW intercepts fetch (src/__tests__/integration/msw/server.js) src/**/*.test.jsx, src/__tests__/integration/**
End-to-end npm run test:e2e Playwright / chromium page.route('**/api/**', …) (e2e/mock-backend.js) e2e/**/*.e2e.js
  • Integration (RTL + MSW): renders real component trees (command palette, assistant panel, Drive, settings, notifications, auth) wired to their real providers, with only the network boundary mocked. The assistant SSE stream is answered with a real ReadableStream, so agentStream.js runs for real. These run under npm run test alongside the unit tests.
  • E2E (Playwright): builds the app, serves it with vite preview, and drives a real Chromium through boot/login, window management, and the ⌘K palette. First run needs the browser: npx playwright install --with-deps chromium. Use npm run test:e2e:ui for the interactive runner. CI runs it via .github/workflows/e2e.yml.

Both layers assert the wave-13 assistant security contract at the UI level: approving an AI proposal posts only the opaque proposal id to /api/assistant/execute (never client-supplied args), and rejecting sends nothing.

Frozen invariants (enforced in review): no CGO in OSS Go code; frontend is JSX only (no .tsx); no Google SSO/OAuth login; billing lives in vulos-cloud, not here.


Self-hosting

Vulos is built to be owned end to end. Deploy it to your own server:

./build.sh --deploy YOUR_SERVER_IP --domain os.yourdomain.com --dns-namecheap USER APIKEY

Or flash a signed image to bare metal:

gunzip -c vulos-vX.X.X-x86_64.img.gz | sudo dd of=/dev/sdX bs=4M status=progress

The image is forkable: supply your own trust-anchor key and bucket URL for a fully independent build. See docs/DEPLOY.md and docs/SELF-HOST-BUNDLE.md.

The OS is the shell; Workspace is a hosted app

The Vulos OS is the shell — the launcher, window manager, dock, assistant, notification center, global ⌘K, and system apps all live here (src/). Opening http://YOUR_BOX:8080/ serves this React window-manager desktop. It is the one shell for the box, whether you're local or remote.

Vulos Workspace is one of the apps the OS hosts, not a second shell. It is an opinionated productivity hub — a cockpit that consolidates the collaboration apps (Mail / Calendar / Office / Talk / Meet, plus Files / Board / Search) into a single integrated view. It ships as its own AGPL package (vulos-workspace), registered in the OS App Registry and loaded under the gateway like any other app at /app/vulos-workspace/; http://YOUR_BOX:8080/workspace opens it. It does not paint its own shell chrome (rail, app switcher, login) — the OS provides those — so there is no second login and no competing launcher.

Workspace is served through the auth-enforcing gateway, which injects the box's identity headers and rewrites the app's <base href> so its assets resolve under /app/vulos-workspace/. Workspace's absolute /api/* calls bypass that base tag and resolve to the box's control-plane (this server), so the hosted app always talks to the box it was opened from.


Security

We take security seriously and welcome good-faith research under a documented safe-harbor policy. Report vulnerabilities via GitHub Security Advisories or security@vulos.org. See SECURITY.md and the THREAT-MODEL.md.


Contributing

Contributions are welcome. Pick a task, branch as task/<ID> or feat//fix//docs/, and run go build ./backend/..., npm run build, and go test ./backend/... before opening a PR. The full guide — task format, decision log, and disclosure process — is in CONTRIBUTING.md.


License

MIT — see LICENSE.



Built with purpose. Open by design.

About

A web-first operating system built on Debian — desktop shell, AI assistant, terminal, file manager, remote browser, and a Python/HTML app ecosystem designed for LLM-generated apps. Open by design.

Topics

Resources

License

Contributing

Security policy

Stars

9 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors