Signal Share is a static media-sharing website starter built with plain HTML, CSS, and JavaScript.
- Publishes image, video, and audio posts from a browser form
- Stores uploaded media locally with IndexedDB when no backend is configured
- Preserves likes in local storage
- Supports search, sorting, tag browsing, saved posts, and per-post deletion
- Includes a spotlight panel, creator leaderboard, and format mix dashboard
- Opens posts in a docked mini-player that can expand into a full-screen viewer
- Supports YouTube and Spotify link posts with embedded playback
- Supports email sign-up, sign-in, and activation when Supabase Auth is enabled
- Supports author/admin deletion on the hosted feed when the matching policies are applied
- Supports Direct Messenger push notifications on the website/PWA and Android app when push is configured
- Includes seeded demo content so the feed is not empty on first load
Open index.html directly in a browser, or serve the folder with any static file server.
This repo is now scaffolded for two app paths:
PWA: installable from a browser on Android and desktopCapacitor Android: package the same web app as a native Android app shell
The web app now includes:
site.webmanifestservice-worker.js- install icons in
icons/
Once deployed over HTTPS, browsers that support installable PWAs can offer Install app.
This repo now includes:
capacitor.config.jsonpackage.jsonscripts/prepare-capacitor-web.ps1
The Capacitor flow stages the current static site into dist/, then points the Android shell at that folder.
After installing Node.js and Android Studio on your machine:
npm install
npm run prepare:web
npx cap add android
npx cap sync android
npx cap open androidThen in Android Studio:
- let Gradle sync
- run the app on an emulator or phone
- replace the generated launcher icons if you want custom polished branding
If you change the web app later, refresh the Android shell with:
npm run cap:syncWear OS should be a separate companion app, not a direct copy of the phone UI.
Recommended watch scope:
- unread message list
- quick reply
- playback controls
- simple latest-post glance view
Recommended stack:
- Kotlin
- Jetpack Compose for Wear OS
- the same Supabase backend used by the phone app
Keep the watch experience shallow and glanceable. The current web messenger/feed UI is too heavy to ship directly to a watch unchanged.
This site can run in two modes:
- Local mode: posts stay in the current browser
- Hosted mode: posts and uploaded media are shared through Supabase
To enable hosted posting on GitHub Pages:
- Create a Supabase project.
- In the Supabase SQL editor, run
supabase/schema.sql. - In Supabase Authentication, enable Email as a sign-in provider and keep Confirm email turned on if you want activation emails.
- In Supabase Authentication > URL Configuration:
- Set
Site URLto your GitHub Pages URL - Add your GitHub Pages URL to
Redirect URLs
- Set
- In Supabase Storage, confirm the
mediabucket exists and is public. - Open
config.js. - Fill in:
supabaseUrlsupabaseAnonKey- optional:
adminEmailsfor the frontend admin allowlist
- Upload the updated
config.jsto GitHub along with your other site files.
Keep the default posts table name and media bucket name unless you also edit supabase/schema.sql.
After that:
- new posts will publish to the shared feed
- uploaded videos and audio will play in the docked player
- the feed will refresh on other devices automatically when the page regains focus and on a timed sync loop
- brand-new visitors will see the real hosted feed instead of the seeded demo posts
- authors can delete their own hosted posts, and configured admin accounts can delete any hosted post
The Publish overlay can save Social drafts locally without provider setup. Direct Social posting uses per-user provider connections so users connect their own accounts once, then later posts can be sent from the website or Android wrapper without a provider share handoff.
Apply supabase/schema.sql so Supabase creates the private social_connections and social_oauth_states tables used by the Edge Functions. Deploy both Social functions and keep their names in config.js:
supabase/functions/social-connect/index.tssupabase/functions/social-publish/index.ts
Deploy social-connect with --no-verify-jwt so X and LinkedIn can reach its OAuth callback. Signed-in connection actions are still checked inside the function. Keep normal JWT verification on social-publish.
The connected-account implementation supports direct posting through X, LinkedIn, Facebook Pages, and Instagram accounts returned by each user's OAuth connection. If a provider returns multiple connected accounts, the Socials panel lets the user choose the account before posting.
Set these Edge Function secrets before users connect providers:
SOCIAL_TOKEN_ENCRYPTION_KEY: a long random secret used to encrypt stored provider tokensSOCIAL_ALLOWED_RETURN_ORIGINS: comma-separated browser origins allowed after OAuth callbacks, for example the hosted site origin and local dev origin- X OAuth:
X_OAUTH_CLIENT_ID, optionalX_OAUTH_CLIENT_SECRET - LinkedIn OAuth:
LINKEDIN_OAUTH_CLIENT_ID,LINKEDIN_OAUTH_CLIENT_SECRET - Meta OAuth for Facebook Pages and Instagram accounts:
META_OAUTH_APP_ID,META_OAUTH_APP_SECRET, optionalMETA_GRAPH_API_VERSION
Register the Social connect callback URL in each provider app before testing OAuth:
https://<project-ref>.supabase.co/functions/v1/social-connect
X must grant user scopes for posting and refreshable access, including tweet.write and offline.access. LinkedIn must grant w_member_social; the current connection flow also requests OpenID profile scopes so it can identify the connected member. Meta OAuth needs Page post access for Facebook and Instagram publishing access for Instagram accounts available to that Meta user. Facebook, X, and LinkedIn accept text with an optional link URL in the Social fields. Instagram direct publishing still needs a public image URL.
Direct Messenger now supports two notification paths:
Web Pushfor the website/PWA, including background notifications when the browser supports pushAndroid Pushfor the Capacitor app shell through Firebase Cloud Messaging
Rerun supabase/schema.sql so Supabase creates the push_subscriptions table and the registration RPCs used by the clients.
If you are using the Wear OS companion, rerun it again after watch push updates so android_wear subscriptions are accepted too.
Generate a VAPID key pair:
npx web-push generate-vapid-keysPut the public key in config.js:
webPushPublicKey
Keep the private key for the Supabase Edge Function secret setup below.
This repo now includes:
supabase/functions/send-message-notification/index.ts
Deploy it with the Supabase CLI or dashboard workflow, then set these secrets for the function:
WEB_PUSH_PUBLIC_KEYWEB_PUSH_PRIVATE_KEYWEB_PUSH_SUBJECTFCM_SERVICE_ACCOUNT_JSON
WEB_PUSH_SUBJECT is usually a mailto: value such as mailto:you@example.com.
For the Android app shell:
- Create or reuse a Firebase project.
- Add your Android app package to Firebase.
- Download
google-services.json. - Place it in:
android/app/google-services.json
- Add the full Firebase service-account JSON as the
FCM_SERVICE_ACCOUNT_JSONsecret for the Supabase Edge Function. - Run:
npm run cap:syncThen reopen Android Studio and rebuild the app.
After deployment:
- Sign in to the site or app.
- Open
Direct Messenger. - Allow notifications when prompted.
The messenger will register the current browser/app instance against the signed-in account and use it for future direct-message notifications.
- If the website is open and active, the messenger still uses the in-page chime immediately.
- Closed/background website notifications depend on browser push support. Desktop Chromium browsers work best; on iPhone/iPad, push requires the site to be added to the Home Screen.
- If the push function is not deployed yet, messages still send normally; only the closed-app/background notification part is skipped.
The web app includes a real-time Direct Messenger with support for:
- Local chiming when active in the foreground
- Background push notifications via FCM (Android) or Web Push (Browser)
- Supabase-powered message sync and delivery status
Signal Share features a deeply integrated, "stacked" AI assistant called the Arcade Companion. This AI is not just a chatbot; it is a system-aware agent capable of analyzing your performance and controlling the platform.
- Deep Telemetry Analysis: The AI has real-time access to your gaming performance, including high scores, total playtime, and global ranks across the mini-game suite.
- Rich Context Awareness: It monitors the current state of the application, including the active media playing in the Hero Player, user account status, and UI configuration (e.g., if the messenger is open).
- Programmatic Control: Through the Arcade Action Protocol, the AI can execute system-level commands based on your natural language:
- Feed Automation: Filter and sort the media feed (e.g., "Show me the most popular videos from today").
- Navigation: Instantly jump to specific sections (e.g., "Take me to the global leaderboards").
- System Styling: Change themes and UI states (e.g., "Switch to Midnight theme").
- Messenger Integration: Use the
[COMPOSE]tool to draft messages and focus the communication dock.
- Multimodal Support: Attach images, videos, or documents to your chat for the AI to analyze and discuss using vision-capable local models.
The Arcade Companion is designed to run primarily on Local LLM infrastructure to ensure maximum privacy and zero latency. To enable the full "stacked" AI experience:
The bridge acts as the secure tunnel between the website and your computer.
- Navigate to the
backend/directory. - Create a
.envfile (see.env.exampleif available) and define your secrets:SIGNAL_SHARE_BRIDGE_SECRET=your_secure_passphrase SIGNAL_SHARE_DEVICE_ID=your_hardware_fingerprint SIGNAL_SHARE_LOCAL_LLM_TOKEN=shared_phone_token # Optional LAN mode for phone access: SIGNAL_SHARE_BRIDGE_LAN=true # Optional explicit bind override: # SIGNAL_SHARE_BRIDGE_BIND=0.0.0.0
- Install dependencies and start the bridge:
npm install node backend/server.js - In the app settings, set:
Bridge URL (PC IP)to your computer bridge URL, for examplehttp://192.168.1.50:3000Local LLM Tokento the same value asSIGNAL_SHARE_LOCAL_LLM_TOKEN
Fortress Mode protects your computer from unauthorized remote access:
- Handshake Secret: Only devices providing the matching
X-Bridge-Secretheader can execute system commands. - Hardware Locking: Use the Security Dashboard in the Arcade Sidebar to lock the bridge to your specific device ID. The server will reject any commands coming from other hardware, even if they have your secret key.
- Automatic IP Banning: The
SecurityEngineautomatically applies permanent IP bans to any address attempting path traversal or brute-forcing your handshake.
The bridge is compatible with any OpenAI-standard local server:
- Ollama: Run
ollama serveand ensure your model is pulled. - LM Studio / LocalAI: Start the local server on port
1234or8080. - In the Arcade Companion UI, select your model from the dropdown (e.g., Qwen 3.5, Gemma 4, or DeepSeek R1).
Signal Share can use MCP tools configured by the current PC user in LM Studio while keeping its existing allowlisted bridge actions available. The bridge reads only MCP server labels from that user's LM Studio mcp.json; selecting a server does not grant its tools to ordinary chat messages.
Normal local chat continues to use the OpenAI-compatible endpoint; messages with a selected MCP tool use LM Studio REST API v1 POST /api/v1/chat, which is the LM Studio endpoint that supports installed MCP plugins.
-
In LM Studio 0.4.0 or newer, enable API authentication and enable Allow calling servers from mcp.json in Developer server settings.
-
Add only MCP tools you trust to LM Studio's
mcp.json. Filesystem MCP servers may read or modify every folder they are allowed to access. -
Keep a
SIGNAL_SHARE_BRIDGE_SECRETorSIGNAL_SHARE_LOCAL_LLM_TOKENconfigured inbackend/.env; LM Studio MCP discovery and execution require a matching bridge credential from the browser. -
Add the private LM Studio API token to
backend/.env:SIGNAL_SHARE_LM_STUDIO_API_TOKEN=your_lm_studio_api_token # Optional context length for MCP requests: # SIGNAL_SHARE_LM_STUDIO_MCP_CONTEXT_LENGTH=8000
-
Restart the Signal Share bridge, open the Companion Security dashboard, and select the LM Studio server this browser user may use. The tool panel reports when the private LM Studio token is still required.
-
Authorize exactly one MCP tool for a request by beginning the message with its exact tool name:
/mcp read_file Read ./backend/server.js and summarize the chat route.The backend sends
allowed_tools: ["read_file"]for that request only. A selected server receives no MCP-enabled request unless the message contains an explicit/mcp <tool_name>directive. To authorize a write-capable tool, the message must explicitly name that write tool.
The filesystem MCP example is not a web search tool; web search requires a separate MCP server that provides web access. Do not store the LM Studio API token in frontend files, browser local storage, or source control. Signal Share's existing strict application and media tools continue to use their current bridge allowlists.
Monitor your security status in real-time:
- Open the Arcade Companion Sidebar.
- Click the Security button (shield icon) or use the shortcut in the Main Settings panel.
- View live audit logs, manage active IP bans, and verify your hardware lock status.
Signal Share can control playback on your local PC or Android device through dedicated media bridges.
The repository includes a Node.js-based bridge (backend/server.js) that:
- Binds to your PC's local loopback for secure system-level media control
- Supports Play, Pause, Next, and Previous actions for apps like Spotify and YouTube
- Reports "Now Playing" snapshots (title, artist, artwork) back to the Signal Share UI
- Includes a security-hardened setup script (
setup-companion.bat) for easy installation
The Capacitor Android shell includes a native PhoneNowPlayingHelper:
- Interfaces with the Android MediaSession system to observe background playback
- Provides an "Open on Phone" handoff feature to launch web content directly into native apps
- Supports cross-device synchronization via Supabase real-time actions
The player UI now supports "Mode Locking," which intelligently switches controls between:
Feed Mode: Controls the website's internal video/audio playersMedia Mode: Directs playback commands to your system bridge (PC or Phone) based on the active source toggle (YouTube or Spotify)
- Create a GitHub repository and upload the files in this folder.
- Push the repository to GitHub on the
mainbranch. - In GitHub, open
Settings>Pages. - Under
Build and deployment, selectGitHub Actions. - Push again after the workflow file is present, or run the workflow manually from the
Actionstab.
After deployment, GitHub Pages will publish the site automatically from the workflow in .github/workflows/static.yml.
- Uploaded posts are stored only in the browser profile where they were created unless Supabase is configured.
- Likes and saved posts are still browser-local, even when the media feed is hosted.
- If you update
supabase/schema.sqlafter an earlier setup, rerun it in Supabase so theauthor_id,site_admins, and delete policies are applied.