Skip to content
78 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

The phone app: Chat in full with phone notifications, plus Code and Customize (#4)1/**
2 * Which g1t the app talks to. Hosted g1t is the site `https://g1t.sh` and
3 * the API `https://api.g1t.sh`; a self-hosted g1t sets its own (SITE_URL
4 * and API_URL, apps/api/src/addresses.rs), so the app keeps both.
5 *
6 * The site is where people sign in and approve the app, and where Chat's
7 * routes are; the API is the REST API and the OAuth issuer.
8 *
9 * Kept free of React Native imports so `npm test` runs it under Node.
10 */
11
12export type Server = {
13 /** An origin, no trailing slash: `https://g1t.sh`. */
14 site: string;
15 /** An origin, no trailing slash: `https://api.g1t.sh`. */
16 api: string;
The phone app works with a g1t of your own as well as g1t.sh, and nothing about that shows to people on g1t.sh. Every g1t now says where its parts are at /.well-known/g1t.json (the site, the API and the MCP server, and whether it sends phones notifications; @g1t/contracts/discovery), so someone pointing the app at theirs types only the address they open it at; a g1t from before has none, and the app takes the API to be api. in front of the site, as before. A self-hosted g1t's download page shows a QR code and Open in the g1t app, a g1t://connect?server=<site> link the app asks about before switching, so its people type nothing. The sign-in screen has one button on g1t.sh, with a quiet "Using your own g1t?" at its foot, and says "Signing in to <host>" when pointed elsewhere.17 /** False when this g1t sends phones no notifications (its PHONE_PUSH=off, lib/discover.ts). */
18 push?: boolean;
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)19};
20
21export const HOSTED: Server = { site: "https://g1t.sh", api: "https://api.g1t.sh" };
22
23/**
24 * An origin as typed: http or https with a host, `https://` when no scheme
25 * is given; only the origin is kept. Null when it is not a web address.
26 * Plain http is accepted for a g1t on this network, as in development.
27 */
28export function parseOrigin(text: string): string | null {
29 const trimmed = text.trim();
30 if (!trimmed) return null;
31 const withScheme = trimmed.includes("://") ? trimmed : `https://${trimmed}`;
32 let url: URL;
33 try {
34 url = new URL(withScheme);
35 } catch {
36 return null;
37 }
38 if (url.protocol !== "https:" && url.protocol !== "http:") return null;
39 if (!url.hostname) return null;
40 return `${url.protocol}//${url.host}`;
41}
42
43/**
44 * The API's address for a site, when only the site was given: hosted
45 * g1t's own, or `api.` in front of a self-hosted site's host, which is
46 * how deploy/self-host names it.
47 */
48export function apiFor(site: string): string {
49 if (site === HOSTED.site) return HOSTED.api;
50 const url = new URL(site);
51 return `${url.protocol}//api.${url.host}`;
52}
53
54/** A server from what was typed: the site, and the API if it was given. */
55export function parseServer(siteText: string, apiText?: string): Server | null {
56 const site = parseOrigin(siteText);
57 if (!site) return null;
58 if (apiText && apiText.trim()) {
59 const api = parseOrigin(apiText);
60 if (!api) return null;
61 return { site, api };
62 }
63 return { site, api: apiFor(site) };
64}
65
66export function isHosted(server: Server): boolean {
67 return server.site === HOSTED.site && server.api === HOSTED.api;
68}
69
70/** The site's address for a path, such as a page to open in the browser. */
71export function siteUrl(server: Server, path: string): string {
72 return server.site + (path.startsWith("/") ? path : `/${path}`);
73}
74
75/** The socket address for one of the site's live paths. */
76export function socketUrl(server: Server, path: string): string {
77 return siteUrl(server, path).replace(/^http/, "ws");
78}

This file's history is long; its oldest lines are credited to the oldest commit read.