# InferencePort AI — Web Interface HuggingFace Docker Space — Node.js WebSocket chat interface for InferencePort AI. ## Environment Variables Set these as **Secrets** in your HuggingFace Space settings: | Variable | Required | Description | |---|---|---| | `SUPABASE_ANON_KEY` | Optional | Supabase anon key (falls back to hardcoded value from app) | | `PUBLIC_URL` | Recommended | Full public URL of your Space, e.g. `https://your-user-your-space.hf.space` | No `SUPABASE_SERVICE_ROLE_KEY` is needed — all DB operations use the user's own access token via RLS. ## HuggingFace Space Setup 1. Create a new **Docker** Space on HuggingFace 2. Push this repository to the Space 3. Set `PUBLIC_URL` secret to your space URL (needed for share links) 4. The app listens on port `7860` which HuggingFace maps automatically ## Supabase Tables Required Run this SQL in your Supabase project's **SQL Editor**: ```sql -- Web sessions (separate from Electron app's chat_sessions) CREATE TABLE IF NOT EXISTS public.web_sessions ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, name text NOT NULL DEFAULT 'New Chat', history jsonb DEFAULT '[]', model text, created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now() ); ALTER TABLE public.web_sessions ENABLE ROW LEVEL SECURITY; CREATE POLICY "Users can manage their own web sessions" ON public.web_sessions FOR ALL USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id); -- Shared sessions (readable by anyone, writable by owner) CREATE TABLE IF NOT EXISTS public.shared_sessions ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), token text UNIQUE NOT NULL, owner_id uuid REFERENCES auth.users(id) ON DELETE CASCADE, session_snapshot jsonb NOT NULL, created_at timestamptz DEFAULT now() ); ALTER TABLE public.shared_sessions ENABLE ROW LEVEL SECURITY; CREATE POLICY "Anyone can read shared sessions" ON public.shared_sessions FOR SELECT USING (true); CREATE POLICY "Owners can insert shared sessions" ON public.shared_sessions FOR INSERT WITH CHECK (auth.uid() = owner_id); -- User settings (theme, tool toggles) CREATE TABLE IF NOT EXISTS public.user_settings ( user_id uuid PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, settings jsonb DEFAULT '{}', updated_at timestamptz DEFAULT now() ); ALTER TABLE public.user_settings ENABLE ROW LEVEL SECURITY; CREATE POLICY "Users manage own settings" ON public.user_settings FOR ALL USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id); ``` The `profiles` table already exists from the Electron app and is reused as-is. ## File Structure ``` inferenceport-web/ ├── Dockerfile ├── package.json ├── README.md ├── server/ │ ├── index.js — Express + WebSocket server entry point │ ├── wsHandler.js — All WebSocket message routing │ ├── sessionStore.js — In-memory + Supabase session storage │ ├── chatStream.js — Lightning API streaming + tool execution │ ├── auth.js — Supabase auth verification, profile, subscription │ └── rateLimiter.js — Simple sliding-window rate limiter └── public/ ├── index.html — Single page app shell ├── oauth-callback.html — OAuth redirect handler ├── css/ │ ├── tokens.css — CSS variables (colors, spacing, fonts) │ ├── base.css — Reset, global styles, animations │ ├── sidebar.css — Collapsible sidebar, session list │ ├── chat.css — Chat messages, code blocks, media │ ├── input.css — Center and bottom input bars │ └── modals.css — Modal overlays, settings, auth forms └── js/ ├── ws.js — WebSocket client (auto-reconnect, request/response) ├── auth.js — Auth state, Supabase sign-in/out, OAuth ├── sessions.js — Session list management and rendering ├── chat.js — Chat rendering, streaming, versioning, editing ├── ui.js — Notifications, context menus, markdown renderer ├── modals.js — All modal dialogs (auth, share, settings, tool detail) ├── settings.js — Settings modal, theme application └── app.js — Bootstrap, input handling, sidebar, paste/attach ``` ## Architecture Notes - **WebSocket** is the primary transport for all chat, auth, and session operations - **Server memory** stores active sessions; Supabase is the persistence layer for logged-in users - **Guest users** get a `tempId` stored in `localStorage`; sessions live in server memory for 12h inactivity / 24h max, with a 15-message daily limit - **On login**, temp sessions are transferred to the user's Supabase account automatically - **Chat versioning**: each message stores `versions[]`, where each version contains the message content plus the `tail` (all following messages at time of edit). Navigating versions via the `‹ 1/N ›` arrows replays the stored tail - **Device sessions** are tracked in server memory only (lost on restart) — no service role key needed - **Lightning** at `https://sharktide-lightning.hf.space/gen` is used for all chat streaming and media generation ## Local Development ```bash npm install PUBLIC_URL=http://localhost:7860 npm run dev ``` Then open `http://localhost:7860`.