Skip to content

Repository files navigation

Hiraya

Hiraya is an installable desktop built with React, TypeScript, Vite, OPFS, and SQLite. It runs browser-local or synchronizes a catalog of named desktops with a same-origin Hiraya server.

The user-oriented Hiraya User Guide is checked in and bundled into the app for offline Help. It covers the product model, hierarchy import, desktops and areas, sharing, offline storage, installation, apps, export, backup and recovery, and troubleshooting.

Development

bun install
bun run dev

The synchronized build uses root-relative /api routes and Vite proxies them to http://127.0.0.1:8080. Run without a server with:

HIRAYA_FRONTEND_ONLY=true bun run dev

Checks

bun test
bun run lint
bun run build

Server Contract

The frontend accepts only remote schema version 1. In synchronized mode it first fetches GET /api/auth/session, which returns a stable opaque storageId, user display metadata, and the required capabilities.blobTransfer: "direct-b2-v1". A 401 redirects to the server-owned /login page with a root-relative return path. GET /api/catalog returns owned and shared desktops with roles and explicit capabilities. Desktop state is read and mutated through /api/desktops/{desktopId} and its canonical scoped resources. Anonymous read-only publications use /shared/{token} and /api/public/desktops/{token} without entering authenticated storage. Events use the catalog SSE event; authenticated /api/sync/health polling remains a fallback, while /api/health remains the public build-health route.

A fresh synchronized browser discovers the server-created first empty desktop through the catalog and projects that desktop into its local cache. If the first catalog request is unavailable, it atomically creates a usable offline desktop and an unbound create-desktop record; the first successful catalog fetch binds and replays that record. The active desktop ID is tab-local sessionStorage state.

System Apps And Offline Storage

The build validates, packages, catalogs, and Workbox-precaches six trusted apps under system-apps/. They are available independently of desktop entries and provide default handlers for folders, text, Markdown, images, PDF, audio, video, and generic files. User-selected file associations and app-local data are browser/account-local rather than desktop-synchronized.

The browser hashes the session storageId into a safe account namespace before loading the desktop or starting workers. The OPFS directory tree, SQLite pool and database, file and pending directories, content markers, workers, locks, preferences, and active desktop session key are all scoped by that namespace. OPFS schema version 5 migrates older versions in place, persists desktop roles so cached shared desktops remain read-only after an offline restart, stores source-aware app installs and normalized file associations, and preserves app storage across bundled app updates. Frontend-only mode makes no auth request and preserves its existing unscoped local storage contract. Logout preserves every account namespace. Synchronized builds remove the old unscoped server-cache layout once rather than migrating it.

Offline mutations update desktop rows and append a strict schema version 1 outbox record atomically. Every record has a desktopId; catalogId is nullable only before the first successful catalog fetch. Replay selects records belonging to the active desktop authority without blocking pending work for other authorities. Shared writes require an online connection. File bytes are staged before metadata is exposed; downloaded bytes are accepted only for the matching catalog, desktop, entry revision, and size. The browser cache and outbox are not a backup.

User mutations return after that durable local commit; server replay, upload, reconciliation, and retry are never part of the interaction's critical path. See Background Synchronization Decision for the canonical interaction contract, failure semantics, and exceptions.

Synchronized file creates and content saves calculate SHA-256 and MD5 from staged OPFS bytes, reserve an atomic blob mutation with the same durable outbox identity, PUT files directly to the server-authorized object-store targets, and finalize metadata only after server verification. Reads obtain a short-lived content-access target, download directly, verify SHA-256, and then populate the OPFS cache. Presigned targets and object-store credentials are never persisted.

Routes And Areas

The canonical pathname is /desktops/{desktopId}/areas/{column}/{row} with optional explorer, file, properties, or settings suffixes. Routing uses the browser History API; hashes are not routes. The transient ?open= file-path query is replaced with the corresponding canonical file pathname after resolution. The static host must serve index.html for direct requests to desktop paths; GitHub Pages does not provide the required fallback. Root coordinates form one continuous canvas; visible areas are derived segments and are not persisted.

Portable Data

Seeded packages, clipboard archives, window sessions, and browser history use strict schema version 1 envelopes. Entries require createdAt. Earlier versions and aliases are intentionally rejected.

Set HIRAYA_SEEDED_DIR to a package directory containing manifest.json and referenced content:

HIRAYA_SEEDED_DIR=examples/seeded bun run build

Seeded content is used only for a fresh frontend-only origin. Synchronized installs converge from the server catalog.

The production build also packages the Calculator, ZIP Browser, Pixel Editor, Project Studio, and Hiraya POS examples into dist/experimental-apps. These archives are publication artifacts for deployment administrators; the server does not create or seed an app desktop automatically.

Releases

Packages

Contributors

Languages