mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-19 17:25:19 +03:00
A host's hostname was its primary key. That worked and disclosed three things it should not have: ids here are monotonic, so an id in a hostname tells anyone who reads a URL roughly when that machine was registered and where it falls among its owner's others; an id is the primary key, so a name that had to change could only change by re-registering the machine, which is changing its identity to fix its name; and the hostname is also the OAuth audience and the cookie scope, so the id travelled into redirect URLs and browser history. Machines now carry a minted name -- two words and four digits, unique across the fleet, DNS-safe by construction, which an id was not. The words are a curated list rather than a dictionary, because every pair is shown to strangers. Names the fleet's own infrastructure answers on are refused at mint time: one minted onto the edge's own label would take the published key path away from every host at once. Minting retries on the unique index rather than checking first, because two registrations in the same instant both read "free" and both write. Only a name collision retries; a duplicate id or secret means something a new name cannot fix. The migration adds the column in three steps. Generated as a NOT NULL column it fails outright against a populated table, and a default would be worse: every row would share one value on a routing key.
192 lines
4.7 KiB
TypeScript
192 lines
4.7 KiB
TypeScript
import { Identifier } from './id.js';
|
|
|
|
export namespace Examples {
|
|
// The width is taken from the generator rather than typed out. Counting
|
|
// twenty-six of anything by eye is a thing people get wrong once and then
|
|
// never look at again — this was one short, which made every documented id
|
|
// a value the schema that published it would reject.
|
|
export const Id = (prefix: keyof typeof Identifier.prefixes) =>
|
|
`${Identifier.prefixes[prefix]}_${'X'.repeat(Identifier.LENGTH)}`;
|
|
|
|
export const User = {
|
|
id: Id('user'),
|
|
name: 'John Doe',
|
|
email: 'johndoe@example.com',
|
|
emailVerified: true,
|
|
image: 'https://cdn.discordapp.com/avatars/xxxxxxx/xxxxxxx.png'
|
|
};
|
|
|
|
export const LinkedAccount = {
|
|
id: Id('linkedAccount'),
|
|
userId: Id('user'),
|
|
provider: 'steam',
|
|
providerAccountId: '76561197960287930',
|
|
profile: { personaname: 'John Doe', avatarfull: 'https://avatars.steamstatic.com/xxxx.jpg' }
|
|
};
|
|
|
|
export const Team = {
|
|
id: Id('team'),
|
|
name: 'The A Team',
|
|
slug: 'the-a-team',
|
|
ownerId: Id('user'),
|
|
billingEmail: 'billing@example.com',
|
|
plan: 'free',
|
|
subscriptionStatus: 'active',
|
|
metadata: null
|
|
};
|
|
|
|
export const Member = {
|
|
id: Id('teamMember'),
|
|
teamId: Id('team'),
|
|
userId: Id('user'),
|
|
role: 'owner' as const
|
|
};
|
|
|
|
export const Fingerprint = {
|
|
id: Id('userFingerprint'),
|
|
userId: Id('user'),
|
|
fingerprint: 'a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4',
|
|
name: 'MacBook Air',
|
|
lastSeen: '2026-07-28T12:00:00.000Z'
|
|
};
|
|
|
|
export const PairingCode = {
|
|
id: Id('pairingCode'),
|
|
code: 'NESSH-7F2Q',
|
|
targetUserId: Id('user'),
|
|
newFingerprint: null,
|
|
expiresAt: '2026-07-28T12:10:00.000Z',
|
|
claimedAt: null,
|
|
isClaimed: false
|
|
};
|
|
|
|
export const Game = {
|
|
id: Id('game'),
|
|
steamAppId: 730,
|
|
slug: 'counter-strike-2',
|
|
name: 'Counter-Strike 2',
|
|
aliases: 'cs2 csgo',
|
|
type: 'game',
|
|
clientIcon: '5aad412d01a9b91ba0379f0b35f4eb0b69d9db08',
|
|
icon: 'f92b09dab91f1d1738f72fe0dd9be18dcc2901f9',
|
|
shortDescription: 'For 25 years...',
|
|
description: 'For over two decades...',
|
|
developers: ['Valve'],
|
|
publishers: ['Valve'],
|
|
primaryGenre: 'Action',
|
|
genres: ['Action', 'FPS'],
|
|
categories: ['Multi-player', 'Steam Achievements'],
|
|
oslist: ['windows', 'linux'],
|
|
sizeDownload: 35000000000,
|
|
sizeOnDisk: 40000000000,
|
|
controllerSupport: 'partial',
|
|
steamDeckCompat: 'perfect',
|
|
reviewScorePercent: 86,
|
|
reviewCount: 1500000,
|
|
metacriticScore: 89,
|
|
steamChangeNumber: 24605165,
|
|
publicBuildId: 12345678,
|
|
releaseDate: '2012-08-21T00:00:00.000Z',
|
|
timeEnriched: '2026-07-29T12:00:00.000Z'
|
|
};
|
|
|
|
export const Library = {
|
|
id: Id('userLibrary'),
|
|
userId: Id('user'),
|
|
gameId: Id('game'),
|
|
playtime2w: 3600,
|
|
playtimeForever: 150000,
|
|
lastPlayed: '2026-07-28T12:00:00.000Z'
|
|
};
|
|
|
|
export const Depot = {
|
|
id: Id('gameDepot'),
|
|
gameId: Id('game'),
|
|
depotId: 731,
|
|
branch: 'public',
|
|
steamManifestId: '3183503801510301321',
|
|
steamBuildId: 12345678,
|
|
installedManifestId: '3183503801510301321',
|
|
installedBuildId: 12345678,
|
|
sizeDownload: 1000,
|
|
sizeOnDisk: 2000,
|
|
status: 'complete' as const,
|
|
errorMessage: null,
|
|
oslist: 'linux'
|
|
};
|
|
|
|
export const AccessToken = {
|
|
id: Id('accessToken'),
|
|
ownerUserId: Id('user'),
|
|
teamId: null,
|
|
name: 'living-room-box',
|
|
expiresAt: null,
|
|
lastUsed: '2026-07-28T12:00:00.000Z'
|
|
};
|
|
|
|
export const Machine = {
|
|
id: Id('machine'),
|
|
ownerUserId: Id('user'),
|
|
teamId: Id('team'),
|
|
label: 'living-room-box',
|
|
slug: 'amber-otter-4821',
|
|
lastSeen: '2026-07-28T12:00:00.000Z',
|
|
endpointId: 'a'.repeat(64)
|
|
};
|
|
|
|
export const SteamEnrolment = {
|
|
machineId: Id('machine'),
|
|
userId: Id('user'),
|
|
steamId: '76561197960287930',
|
|
state: 'enrolled' as const,
|
|
enrolledAt: '2026-07-28T12:00:00.000Z',
|
|
lastOkAt: null,
|
|
revokedAt: null
|
|
};
|
|
|
|
export const Box = {
|
|
id: Id('box'),
|
|
userId: Id('user'),
|
|
machineId: Id('machine'),
|
|
label: 'living room',
|
|
tier: 'sm' as const,
|
|
state: 'created' as const,
|
|
stopReason: null,
|
|
stopClean: null
|
|
};
|
|
|
|
export const Session = {
|
|
id: Id('session'),
|
|
boxId: Id('box'),
|
|
gameId: Id('game'),
|
|
linkedAccountId: Id('linkedAccount'),
|
|
state: 'live' as const,
|
|
ticket: 'nodeaaqf…',
|
|
// Shaped like the real thing so the docs do not teach a shorter one,
|
|
// and obviously not one.
|
|
claimToken: '00000000000000000000000000000000',
|
|
timeStarted: '2026-07-28T12:00:00.000Z',
|
|
timeStopped: null,
|
|
errorMessage: null
|
|
};
|
|
|
|
export const GameDownload = {
|
|
id: Id('gameDownload'),
|
|
hostId: Id('machine'),
|
|
gameId: Id('game'),
|
|
status: 'downloading' as const,
|
|
progressBytes: 1073741824,
|
|
totalBytes: 5000000000,
|
|
timeStarted: '2026-07-28T12:00:00.000Z',
|
|
timeCompleted: null,
|
|
errorMessage: null
|
|
};
|
|
|
|
export const WaitlistEntry = {
|
|
id: Id('waitlistEntry'),
|
|
email: 'johndoe@example.com',
|
|
source: 'machines',
|
|
timeCreated: '2026-07-28T12:00:00.000Z'
|
|
};
|
|
}
|