Skip to content
146 linesCodeBlameRaw
1/**
2 * Who makes g1t, how to reach them, and the policies that govern it: the
3 * one place the footer, the policy pages, /security, /support and
4 * security.txt read these from.
5 */
6import { type SimpleIcon, siX } from "simple-icons";
7
8/** The company behind g1t. */
9export const COMPANY = {
10 name: "Flagon, Inc.",
11 url: "https://www.flagon.io",
12 /** How the footer describes it. */
13 about: "a small, independent software company",
14 /** Who founded it, named wherever the company is described. */
15 founder: "Chase Pierce",
16};
17
18/** g1t's own accounts elsewhere, drawn as their marks in the footer. */
19export const SOCIAL: { label: string; url: string; icon: SimpleIcon }[] = [{ label: "X", url: "https://x.com/g1t_sh", icon: siX }];
20
21/**
22 * Flagon's other launched products, which the footer's maker line names
23 * ("…, the people behind X and Y"). None yet: add each here when it ships.
24 */
25export const MAKER_PRODUCTS: { name: string; url: string }[] = [];
26
27/** "X", "X and Y", "X, Y and Z". */
28export function listed(names: string[]): string {
29 if (names.length <= 1) return names.join("");
30 return `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
31}
32
33/** The year in UTC, so a page rendered near midnight on 31 December agrees everywhere. */
34export function copyrightYear(now: Date = new Date()): number {
35 return now.getUTCFullYear();
36}
37
38/** The footer's legal line. */
39export function copyright(now: Date = new Date()): string {
40 return `© ${copyrightYear(now)} ${COMPANY.name} All rights reserved.`;
41}
42
43/**
44 * Where people reach g1t. Each must be a real, watched mailbox. For now
45 * every topic goes to Flagon's one address; give a topic its own address
46 * here once the g1t.sh aliases exist.
47 */
48export const CONTACT = {
49 support: "hey@flagon.io",
50 security: "hey@flagon.io",
51 privacy: "hey@flagon.io",
52 billing: "hey@flagon.io",
53 abuse: "hey@flagon.io",
54 legal: "hey@flagon.io",
55};
56
57export type Policy = {
58 slug: string;
59 title: string;
60 summary: string;
61};
62
63/** The policies, in the order the index lists them. Each is `/policies/<slug>`. */
64export const POLICIES: Policy[] = [
65 {
66 slug: "terms",
67 title: "Terms of Service",
68 summary: "The agreement between you and Flagon, Inc. when you use g1t: accounts, your content, agents, paying, and ending.",
69 },
70 {
71 slug: "privacy",
72 title: "Privacy Policy",
73 summary: "What g1t collects, why, where it goes, how long it is kept, and how to see, export or delete it.",
74 },
75 {
76 slug: "acceptable-use",
77 title: "Acceptable Use Policy",
78 summary: "What g1t may not be used for, from mining cryptocurrency to abuse, and what happens when it is.",
79 },
80 {
81 slug: "refunds",
82 title: "Refunds and Cancellation",
83 summary: "Ending the plan, accidental overages and goodwill credits, and when money goes back to your card.",
84 },
85 {
86 slug: "subprocessors",
87 title: "Subprocessors",
88 summary: "The companies that process data for g1t, what each does, and the services you choose to connect.",
89 },
90];
91
92/**
93 * The policies are drafts until g1t's general launch: while this is true,
94 * the policies index and every policy page say so at the top
95 * (components/policies-draft.tsx). Set it to false to take the note down.
96 */
97export const POLICIES_DRAFT = true;
98
99/** When any policy last changed, `YYYY-MM-DD`. */
100export const POLICIES_UPDATED = "2026-10-09";
101
102/** Every change to the policies, newest first. */
103export const POLICY_HISTORY: { date: string; change: string }[] = [
104 {
105 date: "2026-10-09",
106 change:
107 "Privacy Policy: g1t.sh uses HeyCatch for site analytics, with names removed outside the public pages, and one analytics cookie, set in the EU, EEA, UK and Switzerland only after you allow it. Subprocessors: HeyCatch, Inc. added.",
108 },
109 {
110 date: "2026-10-06",
111 change:
112 "Privacy Policy: request logs are kept 7 days, and database history 30 days. Subprocessors: GitHub listed among the integrations you choose, in place of Slack, which g1t does not connect to.",
113 },
114 {
115 date: "2026-10-05",
116 change:
117 "First published: Terms of Service, Privacy Policy, Acceptable Use Policy, Refunds and Cancellation, and Subprocessors.",
118 },
119];
120
121/** `2026-10-05` as "October 5, 2026". */
122export function longDate(day: string): string {
123 const [year, month, date] = day.split("-").map(Number);
124 const months = [
125 "January", "February", "March", "April", "May", "June",
126 "July", "August", "September", "October", "November", "December",
127 ];
128 return `${months[month - 1]} ${date}, ${year}`;
129}
130
131/**
132 * security.txt (RFC 9116). `Expires` is required and should be less than a
133 * year away, so it is always computed: 180 days from now.
134 */
135export function securityTxt(now: Date = new Date(), site = "https://g1t.sh"): string {
136 const expires = new Date(now.getTime() + 180 * 24 * 60 * 60 * 1000);
137 expires.setUTCHours(0, 0, 0, 0);
138 return [
139 `Contact: mailto:${CONTACT.security}`,
140 `Expires: ${expires.toISOString().replace(".000Z", "Z")}`,
141 "Preferred-Languages: en",
142 `Policy: ${site}/security#responsible-disclosure`,
143 `Canonical: ${site}/.well-known/security.txt`,
144 "",
145 ].join("\n");
146}