aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/routes/+page.svelte141
-rw-r--r--src/routes/canaries/canaries.ts107
-rw-r--r--src/routes/canaries/keystore.ts137
-rw-r--r--src/routes/canary/+page.svelte7
-rw-r--r--src/routes/shared.ts289
5 files changed, 354 insertions, 327 deletions
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index ed77840..6d521b5 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -13,11 +13,12 @@
*/
-->
<script lang="ts">
- import logo from "./boot-logo.webp";
- import "./anim.css";
- import { architecture, hostname, tty, versions } from "./distro-info";
- import { login, ttyLines, type RenderBlock, type TTYText } from "./shared";
- import { onDestroy, onMount } from "svelte";
+ import logo from './boot-logo.webp';
+ import './anim.css';
+ import { architecture, hostname, tty, versions } from './distro-info';
+ import { login, ttyLines, type RenderBlock, type TTYText } from './shared';
+ import { onDestroy, onMount } from 'svelte';
+ import { base } from '$app/paths';
const initTagLine = `line flex flex-row flex-wrap gap-3 justify-between`;
@@ -36,8 +37,8 @@
skipAnimation?: boolean;
} = $props();
onMount(() => {
- if (skipAnimation && location.pathname !== "/skip-animation")
- history.replaceState({}, "", "/skip-animation");
+ if (skipAnimation && location.pathname !== base + '/skip-animation')
+ history.replaceState({}, '', base + '/skip-animation');
});
let isScripted = $state(false);
@@ -53,7 +54,7 @@
<span class="text-[{colour}]">*</span>
{/snippet}
{#snippet okTagLeft()}
- {@render tagLeft("#51f051")}
+ {@render tagLeft('#51f051')}
{/snippet}
{#snippet tagRight(innerText: string, innerColour: string)}
<span class="text-[#5f5fff]">[</span>
@@ -61,7 +62,7 @@
<span class="text-[#5f5fff]">]</span>
{/snippet}
{#snippet okTagRight()}
- {@render tagRight("ok", "#51f051")}
+ {@render tagRight('ok', '#51f051')}
{/snippet}
{#snippet okLine(text: string, classes?: string, rightClass?: string)}
<div class="{initTagLine}{classes ? ' ' + classes : ''}">
@@ -90,28 +91,30 @@
? ` underline`
: ' no-underline'} inline"
>{#if section.raw}{@html section.value}{:else}{#each section.value
- .split("\n")
+ .split('\n')
.map( (l, i, a) => (i === a.length - 1 ? [0, l] : [1, l]), ) as [nl, l]}{l}{#if nl}<br
/>{/if}{/each}{/if}</span
>
{/snippet}
{#snippet ttyTextMiddleRenderer(
section: RenderBlock,
- line: TTYText & { kind: "text" },
+ line: TTYText & { kind: 'text' },
)}
{#if section.url}
- {#if typeof section.url === "string"}
- {#if section.url.startsWith("newtab:")}
+ {#if typeof section.url === 'string'}
+ {#if section.url.startsWith('newtab:')}
+ {@const url = section.url.substring(7)}
<a
- href={section.url.substring(7)}
+ href={url.startsWith('/') ? base + url : url}
target="_blank"
rel="noopener noreferrer"
class="no-underline text-inherit"
download={section.dl}>{@render ttyTextInnerRenderer(section)}</a
>
- {:else if section.url.startsWith("currenttab:")}
+ {:else if section.url.startsWith('currenttab:')}
+ {@const url = section.url.substring(11)}
<a
- href={section.url.substring(11)}
+ href={url.startsWith('/') ? base + url : url}
class="no-underline text-inherit"
download={section.dl}>{@render ttyTextInnerRenderer(section)}</a
>
@@ -121,9 +124,9 @@
{:else}
<span
onclick={() =>
- typeof section.url !== "function" ? void 0 : section.url(line)}
+ typeof section.url !== 'function' ? void 0 : section.url(line)}
onkeypress={() =>
- typeof section.url !== "function" ? void 0 : section.url(line)}
+ typeof section.url !== 'function' ? void 0 : section.url(line)}
role="link"
tabindex="0">{@render ttyTextInnerRenderer(section)}</span
>
@@ -133,9 +136,9 @@
{/if}
{/snippet}
{#snippet ttyText(line: TTYText)}
- {#if line.kind === "text"}
- {#if (line.renderrestriction ?? "everywhere") === "everywhere" || line.renderrestriction === "noscript" || (line.renderrestriction === "js-only" && isScripted)}
- {#if line.renderrestriction === "noscript"}
+ {#if line.kind === 'text'}
+ {#if (line.renderrestriction ?? 'everywhere') === 'everywhere' || line.renderrestriction === 'noscript' || (line.renderrestriction === 'js-only' && isScripted)}
+ {#if line.renderrestriction === 'noscript'}
<noscript class="inline-block max-w-[100%] {line.classes.join(' ')}"
>{#each line.value as v}{@render ttyTextMiddleRenderer(
v,
@@ -155,7 +158,7 @@
{/snippet}
<div class="hidden">
- {#each ["inline-block", "block", "flex", "inline-flex", "max-w-full", "font-thin", "font-extralight", "font-light", "font-normal", "font-medium", "font-semibold", "font-bold", "font-extrabold", "font-black"] as c}
+ {#each ['inline-block', 'block', 'flex', 'inline-flex', 'max-w-full', 'font-thin', 'font-extralight', 'font-light', 'font-normal', 'font-medium', 'font-semibold', 'font-bold', 'font-extrabold', 'font-black'] as c}
<span class={c}></span>
{/each}
</div>
@@ -257,51 +260,51 @@
>
</div>
<div class="line">&ZeroWidthSpace;</div>
- {@render okLine("/proc/ is already mounted")}
- {@render okLine("Mounting /run ...")}
- {@render okLine("/run/openrc: creating directory")}
- {@render okLine("/run/lock: creating directory", "openrc-boot-step-1")}
- {@render okLine("/run/lock: correcting owner", "openrc-boot-step-1")}
- {@render okLine("Caching service dependencies ...", "openrc-boot-step-1")}
- {@render okLine("Remounting devtmpfs on /dev ...", "openrc-boot-step-2")}
- {@render okLine("Mounting /dev/mqueue ...", "openrc-boot-step-3")}
- {@render okLine("Mounting security filesystem ...", "openrc-boot-step-4")}
- {@render okLine("Mounting debug filesystem ...", "openrc-boot-step-5")}
+ {@render okLine('/proc/ is already mounted')}
+ {@render okLine('Mounting /run ...')}
+ {@render okLine('/run/openrc: creating directory')}
+ {@render okLine('/run/lock: creating directory', 'openrc-boot-step-1')}
+ {@render okLine('/run/lock: correcting owner', 'openrc-boot-step-1')}
+ {@render okLine('Caching service dependencies ...', 'openrc-boot-step-1')}
+ {@render okLine('Remounting devtmpfs on /dev ...', 'openrc-boot-step-2')}
+ {@render okLine('Mounting /dev/mqueue ...', 'openrc-boot-step-3')}
+ {@render okLine('Mounting security filesystem ...', 'openrc-boot-step-4')}
+ {@render okLine('Mounting debug filesystem ...', 'openrc-boot-step-5')}
{@render okLine(
- "Mounting persistent storage (pstore) filesystem ...",
- "openrc-boot-step-5",
+ 'Mounting persistent storage (pstore) filesystem ...',
+ 'openrc-boot-step-5',
)}
- {@render okLine("Mounting efivarfs filesystem ...", "openrc-boot-step-5")}
- {@render okLine("Starting busybox mdev ...", "openrc-boot-step-6")}
- {@render okLine("Scanning hardware for mdev ...", "openrc-boot-step-6")}
- {@render okLine("Loading hardware drivers ...", "openrc-boot-step-7")}
- {@render okLine("Loading modules ...", "openrc-boot-step-8")}
+ {@render okLine('Mounting efivarfs filesystem ...', 'openrc-boot-step-5')}
+ {@render okLine('Starting busybox mdev ...', 'openrc-boot-step-6')}
+ {@render okLine('Scanning hardware for mdev ...', 'openrc-boot-step-6')}
+ {@render okLine('Loading hardware drivers ...', 'openrc-boot-step-7')}
+ {@render okLine('Loading modules ...', 'openrc-boot-step-8')}
{@render okLine(
- "Setting system clock using the hardware clock [UTC] ...",
- "openrc-boot-step-9",
+ 'Setting system clock using the hardware clock [UTC] ...',
+ 'openrc-boot-step-9',
)}
- {@render okLine("Checking local filesystems ...", "openrc-boot-step-10")}
+ {@render okLine('Checking local filesystems ...', 'openrc-boot-step-10')}
<div class="line openrc-boot-step-11">
/dev/mapper/bepis: clean, {fsckFileCount}/{fsckTotalCount} files, {blocksCount}/{blocksTotal}
blocks
</div>
{@render okLine(
- "Remounting root filesystem read/write ...",
- "openrc-boot-step-12",
+ 'Remounting root filesystem read/write ...',
+ 'openrc-boot-step-12',
)}
- {@render okLine("Remounting filesystems ...", "openrc-boot-step-13")}
- {@render okLine("Activating swap devices ...", "openrc-boot-step-14")}
- {@render okLine("Mounting local filesystems ...", "openrc-boot-step-15")}
+ {@render okLine('Remounting filesystems ...', 'openrc-boot-step-13')}
+ {@render okLine('Activating swap devices ...', 'openrc-boot-step-14')}
+ {@render okLine('Mounting local filesystems ...', 'openrc-boot-step-15')}
{@render okLine(
- "Configuring kernel parameters ...",
- "openrc-boot-step-16",
+ 'Configuring kernel parameters ...',
+ 'openrc-boot-step-16',
)}
- {@render okLine("Creating user login records ...", "openrc-boot-step-17")}
- {@render okLine("Setting hostname ...", "openrc-boot-step-18")}
- {@render okLine("Setting keymap ...", "openrc-boot-step-19")}
- {@render okLine("Starting networking ...", "openrc-boot-step-20")}
- {@render okLine("\xa0\xa0lo ...", "openrc-boot-step-21")}
- {@render okLine("\xa0\xa0eth0 ...", "openrc-boot-step-22")}
+ {@render okLine('Creating user login records ...', 'openrc-boot-step-17')}
+ {@render okLine('Setting hostname ...', 'openrc-boot-step-18')}
+ {@render okLine('Setting keymap ...', 'openrc-boot-step-19')}
+ {@render okLine('Starting networking ...', 'openrc-boot-step-20')}
+ {@render okLine('\xa0\xa0lo ...', 'openrc-boot-step-21')}
+ {@render okLine('\xa0\xa0eth0 ...', 'openrc-boot-step-22')}
<div class="line openrc-boot-step-22">udhcpd: started</div>
<div class="line openrc-boot-step-23">udhcpd: broadcasting discover</div>
<!-- TODO: configurable subnet uwu -->
@@ -314,21 +317,21 @@
lease time 3600
</div>
{@render okLine(
- "Seeding random number generator ...",
- "openrc-boot-step-26",
+ 'Seeding random number generator ...',
+ 'openrc-boot-step-26',
)}
{@render okLine(
- "Seeding 256 bits without crediting",
- "openrc-boot-step-26",
+ 'Seeding 256 bits without crediting',
+ 'openrc-boot-step-26',
)}
{@render okLine(
- "Saving 256 bits of creditable seed for next boot",
- "openrc-boot-step-26",
+ 'Saving 256 bits of creditable seed for next boot',
+ 'openrc-boot-step-26',
)}
- {@render okLine("Starting logbookd ...", "openrc-boot-step-27")}
- {@render okLine("Starting busybox acpid ...", "openrc-boot-step-28")}
- {@render okLine("Starting busybox crond ...", "openrc-boot-step-29")}
- {@render okLine("Starting busybox ntpd ...", "openrc-boot-step-29")}
+ {@render okLine('Starting logbookd ...', 'openrc-boot-step-27')}
+ {@render okLine('Starting busybox acpid ...', 'openrc-boot-step-28')}
+ {@render okLine('Starting busybox crond ...', 'openrc-boot-step-29')}
+ {@render okLine('Starting busybox ntpd ...', 'openrc-boot-step-29')}
<div class="line openrc-boot-step-30">&ZeroWidthSpace;</div>
<div class="line openrc-boot-step-30">
Welcome to Alpine Linux {versions.alpine
@@ -341,7 +344,7 @@
<div class="line openrc-boot-step-30">
{hostname} login:&nbsp;<span class="openrc-username"
>{#each login.username
- .split("")
+ .split('')
.map((v, i) => [v, i] as const) as [char, idx]}<span
class="openrc-username-char openrc-username-char-{idx} inline-block"
>{char}</span
@@ -364,16 +367,16 @@
</span>
</div>
<div class="line ttylines-openrc font-mono text-[#070505] flex flex-col">
- {#each ttyLines.filter(((maxidx) => (_, i) => i < (maxidx === -1 ? Infinity : maxidx))(ttyLines.findIndex((v) => v.kind === "clear"))) as line}
+ {#each ttyLines.filter(((maxidx) => (_, i) => i < (maxidx === -1 ? Infinity : maxidx))(ttyLines.findIndex((v) => v.kind === 'clear'))) as line}
{@render ttyText(line)}
{/each}
</div>
</div>
</div>
- {#if ttyLines.find((v) => v.kind === "clear")}
+ {#if ttyLines.find((v) => v.kind === 'clear')}
{#each ttyLines
.map((v, i) => [v, i] as const)
- .filter(([v]) => v.kind === "clear")
+ .filter(([v]) => v.kind === 'clear')
.map((v, i, a) => [v[1], a[i + 1]?.[1], i]) as [idx, nextIdx, clearIdx]}
<div
id="tty-{clearIdx}"
diff --git a/src/routes/canaries/canaries.ts b/src/routes/canaries/canaries.ts
index 692abc2..dab2e68 100644
--- a/src/routes/canaries/canaries.ts
+++ b/src/routes/canaries/canaries.ts
@@ -1,25 +1,25 @@
-import { browser } from "$app/environment";
-import { validateSignature } from "./keystore";
+import { browser } from '$app/environment';
+import { base } from '$app/paths';
+import { validateSignature } from './keystore';
export const canaries: Canary[] = [];
export interface CanaryInterface {
- name: string,
- description: string,
- signer: string,
- url: string,
- keyIdentifier: string,
- contentType: string,
- upstream?: string
+ name: string;
+ description: string;
+ signer: string;
+ url: string;
+ keyIdentifier: string;
+ contentType: string;
+ upstream?: string;
}
-export interface Canary extends CanaryInterface { }
+export interface Canary extends CanaryInterface {}
type _PreCanaryInterfaceEntries = {
- [T in keyof CanaryInterface]: [T, CanaryInterface[T]]
-}
-type CanaryInterfaceEntry = _PreCanaryInterfaceEntries[keyof _PreCanaryInterfaceEntries]
+ [T in keyof CanaryInterface]: [T, CanaryInterface[T]];
+};
+type CanaryInterfaceEntry =
+ _PreCanaryInterfaceEntries[keyof _PreCanaryInterfaceEntries];
export class Canary {
- public constructor(
- canary: CanaryInterface
- ) {
+ public constructor(canary: CanaryInterface) {
const entries = Object.entries(canary) as CanaryInterfaceEntry[];
for (const a of entries) {
// @ts-ignore we know the value will match in type
@@ -29,19 +29,22 @@ export class Canary {
}
public forceContentType = false;
public async getRawText() {
- if (!browser) throw new Error('This should only be done in a browser.')
- const res = await fetch(this.url)
+ if (!browser) throw new Error('This should only be done in a browser.');
+ const res = await fetch(this.url);
if (res.ok) {
- if (!this.forceContentType) this.contentType = res.headers.get('Content-Type') || this.contentType
- const text = await res.text()
- return text
+ if (!this.forceContentType)
+ this.contentType = res.headers.get('Content-Type') || this.contentType;
+ const text = await res.text();
+ return text;
} else {
throw new Error(`Fetching canary failed with code ${res.status} (${res.statusText}):
-${await res.text().catch(e => `Unknown (Unable to get text, ${JSON.stringify(e)})`)}`)
+${await res.text().catch((e) => `Unknown (Unable to get text, ${JSON.stringify(e)})`)}`);
}
}
- public async getValidatedText(rawText: string | Promise<string> = this.getRawText()) {
- return await validateSignature(await rawText, this.keyIdentifier)
+ public async getValidatedText(
+ rawText: string | Promise<string> = this.getRawText(),
+ ) {
+ return await validateSignature(await rawText, this.keyIdentifier);
}
/** Returns downloadable data url if signature passes, otherwise returns null */
public async getUrl(rawText: string | Promise<string> = this.getRawText()) {
@@ -54,34 +57,34 @@ ${await res.text().catch(e => `Unknown (Unable to get text, ${JSON.stringify(e)}
return null;
}
return {
- stripped: URL.createObjectURL(new Blob([stripped], {
- type: this.contentType,
- })),
- signed: URL.createObjectURL(new Blob([t], {
- type: this.contentType,
- })),
- }
+ stripped: URL.createObjectURL(
+ new Blob([stripped], {
+ type: this.contentType,
+ }),
+ ),
+ signed: URL.createObjectURL(
+ new Blob([t], {
+ type: this.contentType,
+ }),
+ ),
+ };
}
}
-new Canary(
- {
- name: 'estrogen.zone',
- description: 'Services hosted around estrogen.zone and yuridick.gay',
- signer: 'memdmp',
- url: '/canaries/memdmp:estrogen.zone',
- keyIdentifier: 'memdmp',
- contentType: 'text/plain; charset=utf-8'
- }
-);
+new Canary({
+ name: 'estrogen.zone',
+ description: 'Services hosted around estrogen.zone and yuridick.gay',
+ signer: 'memdmp',
+ url: base + '/canaries/memdmp:estrogen.zone',
+ keyIdentifier: 'memdmp',
+ contentType: 'text/plain; charset=utf-8',
+});
-new Canary(
- {
- name: 'kyun.host',
- description: 'The VPS provider "kyun.host"',
- signer: 'napatha',
- url: '/canaries/napatha:kyun.host',
- keyIdentifier: 'napatha',
- contentType: 'text/plain; charset=utf-8',
- upstream: 'https://files.kyun.host/canary.txt'
- }
-);
+new Canary({
+ name: 'kyun.host',
+ description: 'The VPS provider "kyun.host"',
+ signer: 'napatha',
+ url: base + '/canaries/napatha:kyun.host',
+ keyIdentifier: 'napatha',
+ contentType: 'text/plain; charset=utf-8',
+ upstream: 'https://files.kyun.host/canary.txt',
+});
diff --git a/src/routes/canaries/keystore.ts b/src/routes/canaries/keystore.ts
index 5b85201..ca8cb22 100644
--- a/src/routes/canaries/keystore.ts
+++ b/src/routes/canaries/keystore.ts
@@ -1,34 +1,49 @@
-import { dev } from "$app/environment";
-import { PublicKey, readCleartextMessage, readKey, verify } from "openpgp";
-import { fallbackKeys } from "./fallback-keys";
+import { dev } from '$app/environment';
+import { PublicKey, readCleartextMessage, readKey, verify } from 'openpgp';
+import { fallbackKeys } from './fallback-keys';
export const keyStore = new Map<string, PublicKey>();
const will_debug = true;
-const debug = dev && will_debug ? (z: any, ...a: any[]) => console.debug(...[
- ...(typeof z === 'string' ? [`[keystore] ${z}`] : ['[keystore]', z]),
- ...a
-]) : () => void 0;
+const debug =
+ dev && will_debug
+ ? (z: any, ...a: any[]) =>
+ console.debug(
+ ...[
+ ...(typeof z === 'string'
+ ? [`[keystore] ${z}`]
+ : ['[keystore]', z]),
+ ...a,
+ ],
+ )
+ : () => void 0;
const _validateSignature = async (message: string, id: string) => {
id = id.toUpperCase();
- debug(`[validateSignature][status=lookup] looking up key by ID ${JSON.stringify(id)}`)
- const key = keyStore.get(id) ?? keyStore.get(id.replace(/ /g, ""));
- if (!key) throw new Error("Could not find key from keystore");
- debug(`[validateSignature][status=parse] parsing message`)
+ debug(
+ `[validateSignature][status=lookup] looking up key by ID ${JSON.stringify(id)}`,
+ );
+ const key = keyStore.get(id) ?? keyStore.get(id.replace(/ /g, ''));
+ if (!key) throw new Error('Could not find key from keystore');
+ debug(`[validateSignature][status=parse] parsing message`);
const signedMessage = await readCleartextMessage({
cleartextMessage: message,
});
- debug(`[validateSignature][status=verify] verifying message signature`)
+ debug(`[validateSignature][status=verify] verifying message signature`);
const verificationResult = await verify({
message: signedMessage,
verificationKeys: key,
expectSigned: true,
});
- debug(`[validateSignature][success] successfully validated message signature`)
+ debug(
+ `[validateSignature][success] successfully validated message signature`,
+ );
return verificationResult.data;
-}
-export const validateSignature: typeof _validateSignature = async (message, id) => {
- debug(`[validateSignature][globalState] waiting on initKeystore`)
+};
+export const validateSignature: typeof _validateSignature = async (
+ message,
+ id,
+) => {
+ debug(`[validateSignature][globalState] waiting on initKeystore`);
await initKeystore;
- return _validateSignature(message, id)
+ return _validateSignature(message, id);
};
const pushKey = async ({
ids,
@@ -47,34 +62,35 @@ const pushKey = async ({
}) => {
ids = ids ?? [];
if (is_url) {
- const url = new URL(key, "https://keys.openpgp.org/vks/v1/by-fingerprint/");
- debug('[pushKey][info] Getting key with url\n\n', url)
- key = await fetch(
- url,
- ).then((v) => v.text()).catch(e => {
- if (fallbackKeys.has(key)) {
- debug('[pushKey][warn] Failed with error', e, 'but found fallback key')
- return fallbackKeys.get(key)!
- }
- else {
- debug('[pushKey][error] Failed to fetch key, cannot find fallback')
- throw e
- }
- });
- debug('[pushKey][success] Fetched key\n\n', JSON.stringify(key))
+ const url = new URL(key, 'https://keys.openpgp.org/vks/v1/by-fingerprint/');
+ debug('[pushKey][info] Getting key with url\n\n', url);
+ key = await fetch(url)
+ .then((v) => v.text())
+ .catch((e) => {
+ if (fallbackKeys.has(key)) {
+ debug(
+ '[pushKey][warn] Failed with error',
+ e,
+ 'but found fallback key',
+ );
+ return fallbackKeys.get(key)!;
+ } else {
+ debug('[pushKey][error] Failed to fetch key, cannot find fallback');
+ throw e;
+ }
+ });
+ debug('[pushKey][success] Fetched key\n\n', JSON.stringify(key));
} else {
- debug('[pushKey][success] Found key\n\n', JSON.stringify(key))
+ debug('[pushKey][success] Found key\n\n', JSON.stringify(key));
}
- if (key === null)
- throw new Error('Key is null.')
- if (key === '')
- throw new Error('Key is empty string.')
+ if (key === null) throw new Error('Key is null.');
+ if (key === '') throw new Error('Key is empty string.');
if (typeof key !== 'string')
- throw new Error(`Expected key with type string, got key of type ${key}`)
+ throw new Error(`Expected key with type string, got key of type ${key}`);
if (signed_by) {
- debug('[pushKey][validation][info] Key must be signed by', signed_by)
+ debug('[pushKey][validation][info] Key must be signed by', signed_by);
key = await _validateSignature(key, signed_by);
- debug('[pushKey][success] Validated signature')
+ debug('[pushKey][success] Validated signature');
}
const parsedKey = await readKey({
armoredKey: key,
@@ -86,22 +102,25 @@ const pushKey = async ({
if (missingUserIds.length) {
throw new Error(
`Key ${parsedKey.getFingerprint()} is missing User IDs: ${missingUserIds.join(
- ", ",
+ ', ',
)}`,
);
}
}
- if (expect_fingerprint && parsedKey.getFingerprint().toUpperCase() !== expect_fingerprint.toUpperCase())
+ if (
+ expect_fingerprint &&
+ parsedKey.getFingerprint().toUpperCase() !==
+ expect_fingerprint.toUpperCase()
+ )
throw new Error(
`Key ${parsedKey.getFingerprint()} is not ${expect_fingerprint}`,
);
else if (expect_fingerprint)
- debug('[success] Fingerprint matches expected fingerprint')
- else
- debug('[warn] No expected fingerprint passed')
+ debug('[success] Fingerprint matches expected fingerprint');
+ else debug('[warn] No expected fingerprint passed');
ids.push(
- parsedKey.getKeyID().toHex().replace(/ /g, ""),
- parsedKey.getFingerprint().replace(/ /g, ""),
+ parsedKey.getKeyID().toHex().replace(/ /g, ''),
+ parsedKey.getFingerprint().replace(/ /g, ''),
...(expect_user_ids ?? []),
);
ids = ids.filter((v, i, a) => a.indexOf(v) === i).map((v) => v.toUpperCase());
@@ -111,15 +130,15 @@ const pushKey = async ({
debug('[success] Added key\n\n', {
key: parsedKey,
ids,
- })
+ });
};
export const initKeystore = (async () => {
await pushKey({
key: 'B546778F06BBCC8EC167DB3CD919706487B8B6DE',
- ids: ["memdmp"],
+ ids: ['memdmp'],
expect_user_ids: [
- "memdmp <memdmp@estrogen.zone>",
- "memdmp <memdmp@memeware.net>",
+ 'memdmp <memdmp@estrogen.zone>',
+ 'memdmp <memdmp@memeware.net>',
],
expect_fingerprint: 'B546778F06BBCC8EC167DB3CD919706487B8B6DE',
is_url: true,
@@ -158,18 +177,18 @@ ZQ4KTbprMz8J4AD/bG33f9Kqg3AqehEyU2TldJs9U9Oni5AXGSGfKLJhmQc=
=945T
-----END PGP SIGNATURE-----
`,
- signed_by: "memdmp <memdmp@memeware.net>",
- ids: ["canary-sigkey-signing"],
- expect_fingerprint: '55D3582CAE78601990A8CA1DBFD0F9E61CB7D84E'
+ signed_by: 'memdmp <memdmp@memeware.net>',
+ ids: ['canary-sigkey-signing'],
+ expect_fingerprint: '55D3582CAE78601990A8CA1DBFD0F9E61CB7D84E',
});
await pushKey({
- key: "https://git.estrogen.zone/mem-estrogen-zone.git/plain/static/keys/external/napatha.pgp.sig",
- ids: ["napatha"],
- expect_user_ids: ["chef naphtha <naphtha@kyun.host>"],
+ key: 'https://git.estrogen.zone/mem-estrogen-zone.git/plain/static/keys/external/napatha.pgp.sig',
+ ids: ['napatha'],
+ expect_user_ids: ['chef naphtha <naphtha@kyun.host>'],
is_url: true,
- signed_by: "canary-sigkey-signing",
+ signed_by: 'canary-sigkey-signing',
});
- debug('[success] keystore initialization completed')
+ debug('[success] keystore initialization completed');
})();
export default keyStore;
diff --git a/src/routes/canary/+page.svelte b/src/routes/canary/+page.svelte
index 25c4f6c..2bb9142 100644
--- a/src/routes/canary/+page.svelte
+++ b/src/routes/canary/+page.svelte
@@ -1,9 +1,10 @@
<script lang="ts">
- import { goto } from "$app/navigation";
- import { onMount } from "svelte";
+ import { goto } from '$app/navigation';
+ import { base } from '$app/paths';
+ import { onMount } from 'svelte';
onMount(() =>
- goto("/canaries/", {
+ goto(base + '/canaries/', {
replaceState: true,
}),
);
diff --git a/src/routes/shared.ts b/src/routes/shared.ts
index d3f9750..7098108 100644
--- a/src/routes/shared.ts
+++ b/src/routes/shared.ts
@@ -7,6 +7,7 @@
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
export const biosSteps = 3;
export const biosStepInterval = 1000;
@@ -23,7 +24,7 @@ export const getDelay = (typingInfo: TypingSpeed) =>
(Math.random() * 2 - 1) * typingInfo.typingDeviation +
typingInfo.typingSpeedAvg;
export const login = {
- username: "lain",
+ username: 'lain',
passwordLength: 12,
...typingInfo(100), // one usually has muscle memory for well-known frequently-typed credentials, hence higher wpm
};
@@ -38,34 +39,34 @@ export type RenderBlock = {
url?:
| `newtab:${string}`
| `currenttab:${string}`
- | ((textObj: TTYText & { kind: "text" }) => void);
+ | ((textObj: TTYText & { kind: 'text' }) => void);
bg?: string;
raw?: boolean;
dl?: string;
};
export type TTYText =
| {
- kind: "text";
- renderrestriction?: "everywhere" | "js-only" | "noscript";
+ kind: 'text';
+ renderrestriction?: 'everywhere' | 'js-only' | 'noscript';
value: RenderBlock[];
id: string;
classes: string[];
}
| {
- kind: "removeNode";
+ kind: 'removeNode';
removedId: string;
removedItemClassList: string[];
}
| {
- kind: "time";
+ kind: 'time';
delay: number;
}
| {
- kind: "cursorVisibility";
+ kind: 'cursorVisibility';
visible: boolean;
}
| {
- kind: "clear";
+ kind: 'clear';
};
export type Only<Obj, Keys extends keyof Obj> = {
[k in Keys]: Obj[k];
@@ -90,15 +91,15 @@ export const ttyLines: TTYText[] = (() => {
const byId = new Map<string, TTYText>();
let i = 69420;
let defaultRenderRestriction: Only<
- TTYText & { kind: "text" },
- "renderrestriction"
- >["renderrestriction"] = "everywhere";
- type LimitedRenderBlock = Omit<RenderBlock, "value">;
+ TTYText & { kind: 'text' },
+ 'renderrestriction'
+ >['renderrestriction'] = 'everywhere';
+ type LimitedRenderBlock = Omit<RenderBlock, 'value'>;
type RenderBlockArg = [text: string, options?: LimitedRenderBlock];
/** due to hellish css constraints, each text() call creates its own line - however, each block in a text() is joined on the same line. */
const text = (
globalOptions:
- | Only<TTYText & { kind: "text" }, "renderrestriction">
+ | Only<TTYText & { kind: 'text' }, 'renderrestriction'>
| RenderBlockArg,
...blocks: RenderBlockArg[]
) => {
@@ -109,12 +110,12 @@ export const ttyLines: TTYText[] = (() => {
globalOptions = {};
}
const txt = {
- kind: "text",
+ kind: 'text',
renderrestriction:
globalOptions.renderrestriction ?? defaultRenderRestriction,
value: blocks.map(([t, o]) => ({
value: t,
- colour: "#b9b9b9",
+ colour: '#b9b9b9',
...o,
})),
id,
@@ -126,34 +127,34 @@ export const ttyLines: TTYText[] = (() => {
};
const wait = (time: number) =>
lines.push({
- kind: "time",
+ kind: 'time',
delay: time,
});
const del = (id: string | number) => {
const removedId =
- typeof id === "string" ? id : id >= 0 ? ids[id] : ids[ids.length + id];
+ typeof id === 'string' ? id : id >= 0 ? ids[id] : ids[ids.length + id];
const r = byId.get(removedId);
- if (r?.kind === "text") {
+ if (r?.kind === 'text') {
lines.push({
- kind: "removeNode",
+ kind: 'removeNode',
removedId,
removedItemClassList: r.classes,
});
- r.classes.push("ttytext-removed-after-done");
+ r.classes.push('ttytext-removed-after-done');
}
ids = ids.filter((v) => v !== removedId);
};
const delSince = (id: string | number) => {
const removedId =
- typeof id === "string" ? id : id >= 0 ? ids[id] : ids[ids.length + id];
+ typeof id === 'string' ? id : id >= 0 ? ids[id] : ids[ids.length + id];
const idIndex = ids.indexOf(removedId);
for (let i = idIndex; i < ids.length; i++) {
const removedId = ids[i];
const r = byId.get(removedId);
- if (r?.kind === "text") {
- r.classes.push("ttytext-removed-after-done");
+ if (r?.kind === 'text') {
+ r.classes.push('ttytext-removed-after-done');
lines.push({
- kind: "removeNode",
+ kind: 'removeNode',
removedId,
removedItemClassList: r.classes,
});
@@ -163,13 +164,13 @@ export const ttyLines: TTYText[] = (() => {
};
const getLast = () => {
const v = byId.get(ids[ids.length - 1]);
- if (v?.kind == "text") {
+ if (v?.kind == 'text') {
return v.value;
} else return null;
};
const overwriteLast = (...newValue: RenderBlockArg[]) => {
const v = byId.get(ids[ids.length - 1]);
- const l = v?.kind == "text" ? v.renderrestriction : "everywhere";
+ const l = v?.kind == 'text' ? v.renderrestriction : 'everywhere';
del(-1);
return text(
{
@@ -188,7 +189,7 @@ export const ttyLines: TTYText[] = (() => {
[
v.value,
Object.fromEntries(
- Object.entries(v).filter(([k]) => k !== "value"),
+ Object.entries(v).filter(([k]) => k !== 'value'),
),
] as const,
),
@@ -196,26 +197,26 @@ export const ttyLines: TTYText[] = (() => {
);
const everyTTYClear: (() => void)[] = [];
const clear = () => {
- if (lines.find((v) => v.kind === "clear")) {
- delSince(lines.findLastIndex((v) => v.kind === "clear"));
+ if (lines.find((v) => v.kind === 'clear')) {
+ delSince(lines.findLastIndex((v) => v.kind === 'clear'));
}
- lines.push({ kind: "clear" });
+ lines.push({ kind: 'clear' });
everyTTYClear.forEach((v) => v());
};
everyTTYClear.push(() => {
text(
{
- renderrestriction: "noscript",
+ renderrestriction: 'noscript',
},
[
- "This browser does not support JS. Your experience may be degraded.",
+ 'This browser does not support JS. Your experience may be degraded.',
{
- bg: "#ff0000",
- colour: "#dedede",
+ bg: '#ff0000',
+ colour: '#dedede',
},
],
- ["\n", {}],
+ ['\n', {}],
);
});
everyTTYClear.forEach((v) => v());
@@ -223,94 +224,94 @@ export const ttyLines: TTYText[] = (() => {
//
wait(300);
- text({ renderrestriction: "js-only" }, [
+ text({ renderrestriction: 'js-only' }, [
((v) => v[Math.floor(Math.random() * v.length)])([
- "cool beats are stored in the balls",
- "found xml documents in the firmware",
+ 'cool beats are stored in the balls',
+ 'found xml documents in the firmware',
'overhead: "I don\'t consent" "hey thats my line"',
- "uwu",
- "i regret making this hellhole of a codebase",
+ 'uwu',
+ 'i regret making this hellhole of a codebase',
]),
{
- colour: "#777777",
+ colour: '#777777',
},
]);
wait(1000);
clear();
text(
[
- "lain",
+ 'lain',
{
- colour: "#FFFF53",
+ colour: '#FFFF53',
weight: 700,
},
],
- [" in "],
+ [' in '],
[
- "mem.estrogen.zone",
+ 'mem.estrogen.zone',
{
- colour: "#17B117",
+ colour: '#17B117',
weight: 700,
},
],
- [" in "],
+ [' in '],
[
- "~",
+ '~',
{
- colour: "#53FFFF",
+ colour: '#53FFFF',
weight: 700,
},
],
);
text(
- ["❯ ", { colour: "#53FF53", weight: 600 }],
- [""],
- ["", { colour: "#777777" }],
+ ['❯ ', { colour: '#53FF53', weight: 600 }],
+ [''],
+ ['', { colour: '#777777' }],
);
{
- const targetstr = "/usr/bin/env wel";
+ const targetstr = '/usr/bin/env wel';
const completions = [
'/bin/sh -c "$(/usr/bin/env curl -fsSL https://blahaj.estrogen.zone/)"',
- "/usr/local/bin/become-estradiol",
- "/usr/bin/shellutil ansi cheatsheet 24bit",
- "/usr/bin/env sh",
- "/usr/bin/env wc -l src/routes/anim.css",
- "/usr/bin/env welcome -c ~/.local/share/welcome.toml",
+ '/usr/local/bin/become-estradiol',
+ '/usr/bin/shellutil ansi cheatsheet 24bit',
+ '/usr/bin/env sh',
+ '/usr/bin/env wc -l src/routes/anim.css',
+ '/usr/bin/env welcome -c ~/.local/share/welcome.toml',
];
- for (const c of [...targetstr.split(""), "COMPLETE"]) {
+ for (const c of [...targetstr.split(''), 'COMPLETE']) {
replaceLast((l) => {
const prompt = l[l.length - 2];
const suggestions = l[l.length - 1];
- if (c === "COMPLETE") {
+ if (c === 'COMPLETE') {
prompt[0] += suggestions[0];
- suggestions[0] = "";
+ suggestions[0] = '';
} else {
prompt[0] += c;
const completion = completions.find((v) => v.startsWith(prompt[0]));
if (completion) {
suggestions[0] = completion.substring(prompt[0].length);
- } else suggestions[0] = "";
+ } else suggestions[0] = '';
}
return l;
});
- if (c === "COMPLETE") {
+ if (c === 'COMPLETE') {
wait(100);
} else wait(50 + Math.random() * 100);
}
- text(["Preparing..."]);
+ text(['Preparing...']);
wait(200);
}
wait(1000);
clear();
text(
["Hi! I'm a"],
- [""],
+ [''],
[
" software developer.\nI do shit like the hellhole you're seeing right now, amongst other things.\n\n",
],
);
wait(300);
- for (const c of " transfem".split("")) {
+ for (const c of ' transfem'.split('')) {
replaceLast((l) => {
l[1][0] += c;
return l;
@@ -318,95 +319,95 @@ export const ttyLines: TTYText[] = (() => {
wait(70);
}
text([
- "Places you can find me:",
+ 'Places you can find me:',
{
- colour: "#7a7a7a",
+ colour: '#7a7a7a',
},
]);
wait(300);
text(
[
- " - ",
+ ' - ',
{
- colour: "#7a7a7a",
+ colour: '#7a7a7a',
},
],
- ["estrogen.zone git: ", { colour: "#cdcdcd" }],
+ ['estrogen.zone git: ', { colour: '#cdcdcd' }],
);
wait(100);
replaceLast((v) => [
...v,
[
- "git.estrogen.zone",
+ 'git.estrogen.zone',
{
- colour: "#99aaff",
+ colour: '#99aaff',
underlined: true,
weight: 700,
- url: "newtab:https://git.estrogen.zone",
+ url: 'newtab:https://git.estrogen.zone',
},
],
]);
wait(100);
text(
[
- " - ",
+ ' - ',
{
- colour: "#7a7a7a",
+ colour: '#7a7a7a',
},
],
- ["On jsr: ", { colour: "#cdcdcd" }],
+ ['On jsr: ', { colour: '#cdcdcd' }],
);
wait(600);
replaceLast((v) => [
...v,
[
- "jsr.io/@memdmp",
+ 'jsr.io/@memdmp',
{
- colour: "#f7df23",
+ colour: '#f7df23',
underlined: true,
weight: 700,
- url: "newtab:https://jsr.io/@memdmp",
+ url: 'newtab:https://jsr.io/@memdmp',
},
],
]);
wait(200);
text(
[
- " - ",
+ ' - ',
{
- colour: "#7a7a7a",
+ colour: '#7a7a7a',
},
],
- ["In random hackerspaces", { colour: "#aaaaaa" }],
+ ['In random hackerspaces', { colour: '#aaaaaa' }],
);
wait(600);
text([
- "\nYou may find these useful:",
+ '\nYou may find these useful:',
{
- colour: "#7a7a7a",
+ colour: '#7a7a7a',
},
]);
wait(400);
text(
[
- " - ",
+ ' - ',
{
- colour: "#7a7a7a",
+ colour: '#7a7a7a',
},
],
- ["GPG Root Key: ", { colour: "#cdcdcd" }],
+ ['GPG Root Key: ', { colour: '#cdcdcd' }],
);
wait(1000);
replaceLast((v) => [
...v,
[
- "B546778F06BBCC8EC167DB3CD919706487B8B6DE",
+ 'B546778F06BBCC8EC167DB3CD919706487B8B6DE',
{
- colour: "#58C7F3",
+ colour: '#58C7F3',
underlined: true,
weight: 700,
- url: "currenttab:/keys/memdmp/primary.pgp",
- dl: "memdmp-primary.pgp",
+ url: `currenttab:/keys/memdmp/primary.pgp`,
+ dl: 'memdmp-primary.pgp',
},
],
]);
@@ -414,35 +415,35 @@ export const ttyLines: TTYText[] = (() => {
replaceLast((v) => [
...v,
[
- " (root key)",
+ ' (root key)',
{
- colour: "#999999",
+ colour: '#999999',
},
],
]);
wait(400);
text([
- " - ",
+ ' - ',
{
- colour: "#7a7a7a",
+ colour: '#7a7a7a',
},
]);
wait(70);
replaceLast((v) => [
...v,
- ["GPG Git Key: ", { colour: "#cdcdcd" }],
+ ['GPG Git Key: ', { colour: '#cdcdcd' }],
]);
wait(100);
replaceLast((v) => [
...v,
[
- "5134F05BD8D9DB8C6C0E1515A9439D346AB6DF4E",
+ '5134F05BD8D9DB8C6C0E1515A9439D346AB6DF4E',
{
- colour: "#F0A3B3",
+ colour: '#F0A3B3',
underlined: true,
weight: 700,
- url: "currenttab:/keys/memdmp/git.pgp",
- dl: "memdmp-git.pgp",
+ url: `currenttab:/keys/memdmp/git.pgp`,
+ dl: 'memdmp-git.pgp',
},
],
]);
@@ -450,37 +451,37 @@ export const ttyLines: TTYText[] = (() => {
replaceLast((v) => [
...v,
[
- " (",
+ ' (',
{
- colour: "#999999",
+ colour: '#999999',
},
],
[
- "signed",
+ 'signed',
{
- colour: "#F0A3B3",
+ colour: '#F0A3B3',
underlined: true,
weight: 500,
- url: "currenttab:/keys/memdmp/git.pgp.sig",
- dl: "memdmp-git.pgp.sig",
+ url: `currenttab:/keys/memdmp/git.pgp.sig`,
+ dl: 'memdmp-git.pgp.sig',
},
],
- [")", { colour: "#999999" }],
+ [')', { colour: '#999999' }],
]);
wait(45);
text([
- " - ",
+ ' - ',
{
- colour: "#7a7a7a",
+ colour: '#7a7a7a',
},
]);
wait(70);
replaceLast((v) => [
...v,
[
- "GPG Release Signing Key: ",
+ 'GPG Release Signing Key: ',
{
- colour: "#cdcdcd",
+ colour: '#cdcdcd',
},
],
]);
@@ -488,13 +489,13 @@ export const ttyLines: TTYText[] = (() => {
replaceLast((v) => [
...v,
[
- "0D93102265071798C7B65A4C9F0739B9E0C8FD60",
+ '0D93102265071798C7B65A4C9F0739B9E0C8FD60',
{
- colour: "#ffffff",
+ colour: '#ffffff',
underlined: true,
weight: 700,
- url: "currenttab:/keys/memdmp/release.pgp",
- dl: "memdmp-release.pgp",
+ url: `currenttab:/keys/memdmp/release.pgp`,
+ dl: 'memdmp-release.pgp',
},
],
]);
@@ -502,37 +503,37 @@ export const ttyLines: TTYText[] = (() => {
replaceLast((v) => [
...v,
[
- " (",
+ ' (',
{
- colour: "#999999",
+ colour: '#999999',
},
],
[
- "signed",
+ 'signed',
{
- colour: "#ffffff",
+ colour: '#ffffff',
underlined: true,
weight: 500,
- url: "currenttab:/keys/memdmp/release.pgp.sig",
- dl: "memdmp-release.pgp.sig",
+ url: `currenttab:/keys/memdmp/release.pgp.sig`,
+ dl: 'memdmp-release.pgp.sig',
},
],
- [")", { colour: "#999999" }],
+ [')', { colour: '#999999' }],
]);
wait(100);
text([
- " - ",
+ ' - ',
{
- colour: "#7a7a7a",
+ colour: '#7a7a7a',
},
]);
wait(35);
replaceLast((v) => [
...v,
[
- "Source Code: ",
+ 'Source Code: ',
{
- colour: "#cdcdcd",
+ colour: '#cdcdcd',
},
],
]);
@@ -540,29 +541,29 @@ export const ttyLines: TTYText[] = (() => {
replaceLast((v) => [
...v,
[
- "git.estrogen.zone/mem-estrogen-zone",
+ 'git.estrogen.zone/mem-estrogen-zone',
{
- colour: "#F0A3B3",
+ colour: '#F0A3B3',
underlined: true,
weight: 700,
- url: "newtab:/upstream/",
+ url: 'newtab:/upstream/',
},
],
]);
wait(100);
text([
- " - ",
+ ' - ',
{
- colour: "#7a7a7a",
+ colour: '#7a7a7a',
},
]);
wait(35);
replaceLast((v) => [
...v,
[
- "Canaries: ",
+ 'Canaries: ',
{
- colour: "#cdcdcd",
+ colour: '#cdcdcd',
},
],
]);
@@ -570,12 +571,12 @@ export const ttyLines: TTYText[] = (() => {
replaceLast((v) => [
...v,
[
- "/canaries/",
+ './canaries/',
{
- colour: "#58C7F3",
+ colour: '#58C7F3',
underlined: true,
weight: 700,
- url: "currenttab:/canaries/",
+ url: `currenttab:/canaries/`,
},
],
]);
@@ -584,23 +585,23 @@ export const ttyLines: TTYText[] = (() => {
`<button style="padding: 12px 12px;background: #fff2;margin-top: 0.4rem;border-radius: 0.7rem;opacity:0.1;margin-top:3rem;" data-el="le funny button">have a button :3</button>`,
{
raw: true,
- url: () => alert("i abused too much css for this i wanna cry now"),
+ url: () => alert('i abused too much css for this i wanna cry now'),
},
]);
wait(200);
text(
- { renderrestriction: "js-only" },
+ { renderrestriction: 'js-only' },
[
- "next time, you may want to ",
+ 'next time, you may want to ',
{
- colour: "#fff2",
+ colour: '#fff2',
},
],
[
- `skip the animation<img style="opacity:0;position:fixed;top:0;left:0;width:0px;height:0px;pointer-events:none;" src='about:blank' onerror='setTimeout(()=>{if(location.pathname==="/skip-animation")document.querySelector(${JSON.stringify(`a[href=${JSON.stringify("/skip-animation")}]`)})?.parentElement?.remove();else document.querySelector(${JSON.stringify(`a[href=${JSON.stringify("about:blank")}]`)})?.remove();},1)' />`,
+ `skip the animation<img style="opacity:0;position:fixed;top:0;left:0;width:0px;height:0px;pointer-events:none;" src='about:blank' onerror='setTimeout(()=>{if(location.pathname===${JSON.stringify('/~mem/skip-animation')})document.querySelector(${JSON.stringify(`a[href=${JSON.stringify('/~mem/skip-animation')}]`)})?.parentElement?.remove();else document.querySelector(${JSON.stringify(`a[href=${JSON.stringify('about:blank')}]`)})?.remove();},1)' />`,
{
- url: "currenttab:/skip-animation",
- colour: "#fff2",
+ url: `currenttab:/skip-animation`,
+ colour: '#fff2',
underlined: true,
raw: true,
},