From d57a5985982ed2ba386eb4c0b4ca05ab3498238a Mon Sep 17 00:00:00 2001 From: memdmp Date: Sat, 11 Jan 2025 20:20:16 +0100 Subject: feat: initial commit --- src/app.css | 12 + src/app.d.ts | 13 + src/app.html | 31 ++ src/lib/fonts/Ac437_IBM_EGA_8x14.css | 18 + src/lib/fonts/Ac437_IBM_EGA_8x14.ttf | Bin 0 -> 25540 bytes src/lib/fonts/Ac437_IBM_EGA_8x14.woff | Bin 0 -> 9784 bytes src/lib/fonts/Ac437_IBM_EGA_8x14.woff2 | Bin 0 -> 6608 bytes src/lib/fonts/Px437_DOS-V_re_JPN12.css | 18 + src/lib/fonts/Px437_DOS-V_re_JPN12.ttf | Bin 0 -> 25960 bytes src/lib/fonts/Px437_DOS-V_re_JPN12.woff | Bin 0 -> 9520 bytes src/lib/fonts/Px437_DOS-V_re_JPN12.woff2 | Bin 0 -> 6628 bytes src/lib/fonts/PxPlus_IBM_EGA_8x14.css | 18 + src/lib/fonts/PxPlus_IBM_EGA_8x14.ttf | Bin 0 -> 70172 bytes src/lib/fonts/PxPlus_IBM_EGA_8x14.woff | Bin 0 -> 22356 bytes src/lib/fonts/PxPlus_IBM_EGA_8x14.woff2 | Bin 0 -> 14712 bytes src/lib/fonts/all-local-after-woff2.css | 36 ++ src/lib/fonts/all.css | 36 ++ src/lib/index.ts | 10 + src/lib/license.ts | 7 + src/lib/styles.ts | 10 + src/routes/+layout.svelte | 11 + src/routes/+layout.ts | 11 + src/routes/+page.svelte | 414 +++++++++++++++++++ src/routes/anim-gen.ts | 504 +++++++++++++++++++++++ src/routes/boot-logo.png | Bin 0 -> 212582 bytes src/routes/canaries/+page.svelte | 5 + src/routes/canaries/canaries.ts | 25 ++ src/routes/canaries/keystore.ts | 120 ++++++ src/routes/canaries/napatha:kyun.host/+server.ts | 5 + src/routes/distro-info.ts | 38 ++ src/routes/license/+server.ts | 5 + src/routes/shared.ts | 485 ++++++++++++++++++++++ src/routes/upstream/+server.ts | 6 + 33 files changed, 1838 insertions(+) create mode 100644 src/app.css create mode 100644 src/app.d.ts create mode 100644 src/app.html create mode 100644 src/lib/fonts/Ac437_IBM_EGA_8x14.css create mode 100644 src/lib/fonts/Ac437_IBM_EGA_8x14.ttf create mode 100644 src/lib/fonts/Ac437_IBM_EGA_8x14.woff create mode 100644 src/lib/fonts/Ac437_IBM_EGA_8x14.woff2 create mode 100644 src/lib/fonts/Px437_DOS-V_re_JPN12.css create mode 100644 src/lib/fonts/Px437_DOS-V_re_JPN12.ttf create mode 100644 src/lib/fonts/Px437_DOS-V_re_JPN12.woff create mode 100644 src/lib/fonts/Px437_DOS-V_re_JPN12.woff2 create mode 100644 src/lib/fonts/PxPlus_IBM_EGA_8x14.css create mode 100644 src/lib/fonts/PxPlus_IBM_EGA_8x14.ttf create mode 100644 src/lib/fonts/PxPlus_IBM_EGA_8x14.woff create mode 100644 src/lib/fonts/PxPlus_IBM_EGA_8x14.woff2 create mode 100644 src/lib/fonts/all-local-after-woff2.css create mode 100644 src/lib/fonts/all.css create mode 100644 src/lib/index.ts create mode 100644 src/lib/license.ts create mode 100644 src/lib/styles.ts create mode 100644 src/routes/+layout.svelte create mode 100644 src/routes/+layout.ts create mode 100644 src/routes/+page.svelte create mode 100644 src/routes/anim-gen.ts create mode 100644 src/routes/boot-logo.png create mode 100644 src/routes/canaries/+page.svelte create mode 100644 src/routes/canaries/canaries.ts create mode 100644 src/routes/canaries/keystore.ts create mode 100644 src/routes/canaries/napatha:kyun.host/+server.ts create mode 100644 src/routes/distro-info.ts create mode 100644 src/routes/license/+server.ts create mode 100644 src/routes/shared.ts create mode 100644 src/routes/upstream/+server.ts (limited to 'src') diff --git a/src/app.css b/src/app.css new file mode 100644 index 0000000..e920bfb --- /dev/null +++ b/src/app.css @@ -0,0 +1,12 @@ +/* + Copyright (C) 2024 memdmp + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/src/app.d.ts b/src/app.d.ts new file mode 100644 index 0000000..743f07b --- /dev/null +++ b/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/src/app.html b/src/app.html new file mode 100644 index 0000000..f2d20ed --- /dev/null +++ b/src/app.html @@ -0,0 +1,31 @@ + + + + + + + + + %sveltekit.head% + + + +
%sveltekit.body%
+ + + \ No newline at end of file diff --git a/src/lib/fonts/Ac437_IBM_EGA_8x14.css b/src/lib/fonts/Ac437_IBM_EGA_8x14.css new file mode 100644 index 0000000..01a053b --- /dev/null +++ b/src/lib/fonts/Ac437_IBM_EGA_8x14.css @@ -0,0 +1,18 @@ +/* + Copyright (C) 2024 memdmp + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ +@font-face { + font-family: "Ac437 IBM EGA 8x14"; + src: + local("Ac437 IBM EGA 8x14"), + url("./Ac437_IBM_EGA_8x14.woff2") format("woff2"), + url("./Ac437_IBM_EGA_8x14.woff") format("woff"), + url("./Ac437_IBM_EGA_8x14.ttf") format("opentype"); + font-display: swap; +} diff --git a/src/lib/fonts/Ac437_IBM_EGA_8x14.ttf b/src/lib/fonts/Ac437_IBM_EGA_8x14.ttf new file mode 100644 index 0000000..d5b008b Binary files /dev/null and b/src/lib/fonts/Ac437_IBM_EGA_8x14.ttf differ diff --git a/src/lib/fonts/Ac437_IBM_EGA_8x14.woff b/src/lib/fonts/Ac437_IBM_EGA_8x14.woff new file mode 100644 index 0000000..2a9d6dc Binary files /dev/null and b/src/lib/fonts/Ac437_IBM_EGA_8x14.woff differ diff --git a/src/lib/fonts/Ac437_IBM_EGA_8x14.woff2 b/src/lib/fonts/Ac437_IBM_EGA_8x14.woff2 new file mode 100644 index 0000000..fa786e2 Binary files /dev/null and b/src/lib/fonts/Ac437_IBM_EGA_8x14.woff2 differ diff --git a/src/lib/fonts/Px437_DOS-V_re_JPN12.css b/src/lib/fonts/Px437_DOS-V_re_JPN12.css new file mode 100644 index 0000000..410e9de --- /dev/null +++ b/src/lib/fonts/Px437_DOS-V_re_JPN12.css @@ -0,0 +1,18 @@ +/* + Copyright (C) 2024 memdmp + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ +@font-face { + font-family: "Px437 DOS/V re. JPN12"; + src: + local("Px437 DOS/V re. JPN12"), + url("./Px437_DOS-V_re_JPN12.woff2") format("woff2"), + url("./Px437_DOS-V_re_JPN12.woff") format("woff"), + url("./Px437_DOS-V_re_JPN12.ttf") format("opentype"); + font-display: swap; +} diff --git a/src/lib/fonts/Px437_DOS-V_re_JPN12.ttf b/src/lib/fonts/Px437_DOS-V_re_JPN12.ttf new file mode 100644 index 0000000..6f4d616 Binary files /dev/null and b/src/lib/fonts/Px437_DOS-V_re_JPN12.ttf differ diff --git a/src/lib/fonts/Px437_DOS-V_re_JPN12.woff b/src/lib/fonts/Px437_DOS-V_re_JPN12.woff new file mode 100644 index 0000000..311517f Binary files /dev/null and b/src/lib/fonts/Px437_DOS-V_re_JPN12.woff differ diff --git a/src/lib/fonts/Px437_DOS-V_re_JPN12.woff2 b/src/lib/fonts/Px437_DOS-V_re_JPN12.woff2 new file mode 100644 index 0000000..12926c4 Binary files /dev/null and b/src/lib/fonts/Px437_DOS-V_re_JPN12.woff2 differ diff --git a/src/lib/fonts/PxPlus_IBM_EGA_8x14.css b/src/lib/fonts/PxPlus_IBM_EGA_8x14.css new file mode 100644 index 0000000..d1a3a97 --- /dev/null +++ b/src/lib/fonts/PxPlus_IBM_EGA_8x14.css @@ -0,0 +1,18 @@ +/* + Copyright (C) 2024 memdmp + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ +@font-face { + font-family: "PxPlus IBM EGA 8x14"; + src: + local("PxPlus IBM EGA 8x14"), + url("./PxPlus_IBM_EGA_8x14.woff2") format("woff2"), + url("./PxPlus_IBM_EGA_8x14.woff") format("woff"), + url("./PxPlus_IBM_EGA_8x14.ttf") format("opentype"); + font-display: swap; +} diff --git a/src/lib/fonts/PxPlus_IBM_EGA_8x14.ttf b/src/lib/fonts/PxPlus_IBM_EGA_8x14.ttf new file mode 100644 index 0000000..6dc1007 Binary files /dev/null and b/src/lib/fonts/PxPlus_IBM_EGA_8x14.ttf differ diff --git a/src/lib/fonts/PxPlus_IBM_EGA_8x14.woff b/src/lib/fonts/PxPlus_IBM_EGA_8x14.woff new file mode 100644 index 0000000..0763445 Binary files /dev/null and b/src/lib/fonts/PxPlus_IBM_EGA_8x14.woff differ diff --git a/src/lib/fonts/PxPlus_IBM_EGA_8x14.woff2 b/src/lib/fonts/PxPlus_IBM_EGA_8x14.woff2 new file mode 100644 index 0000000..c29e71a Binary files /dev/null and b/src/lib/fonts/PxPlus_IBM_EGA_8x14.woff2 differ diff --git a/src/lib/fonts/all-local-after-woff2.css b/src/lib/fonts/all-local-after-woff2.css new file mode 100644 index 0000000..9bc8d65 --- /dev/null +++ b/src/lib/fonts/all-local-after-woff2.css @@ -0,0 +1,36 @@ +/* + Copyright (C) 2024 memdmp + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ +@font-face { + font-family: "Ac437 IBM EGA 8x14"; + src: + url("./Ac437_IBM_EGA_8x14.woff2") format("woff2"), + local("Ac437 IBM EGA 8x14"), + url("./Ac437_IBM_EGA_8x14.woff") format("woff"), + url("./Ac437_IBM_EGA_8x14.ttf") format("opentype"); + font-display: swap; +} +@font-face { + font-family: "PxPlus IBM EGA 8x14"; + src: + url("./PxPlus_IBM_EGA_8x14.woff2") format("woff2"), + local("PxPlus IBM EGA 8x14"), + url("./PxPlus_IBM_EGA_8x14.woff") format("woff"), + url("./PxPlus_IBM_EGA_8x14.ttf") format("opentype"); + font-display: swap; +} +@font-face { + font-family: "Px437 DOS/V re. JPN12"; + src: + url("./Px437_DOS-V_re_JPN12.woff2") format("woff2"), + local("Px437 DOS/V re. JPN12"), + url("./Px437_DOS-V_re_JPN12.woff") format("woff"), + url("./Px437_DOS-V_re_JPN12.ttf") format("opentype"); + font-display: swap; +} diff --git a/src/lib/fonts/all.css b/src/lib/fonts/all.css new file mode 100644 index 0000000..7b30c9a --- /dev/null +++ b/src/lib/fonts/all.css @@ -0,0 +1,36 @@ +/* + Copyright (C) 2024 memdmp + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ +@font-face { + font-family: "Ac437 IBM EGA 8x14"; + src: + local("Ac437 IBM EGA 8x14"), + url("./Ac437_IBM_EGA_8x14.woff2") format("woff2"), + url("./Ac437_IBM_EGA_8x14.woff") format("woff"), + url("./Ac437_IBM_EGA_8x14.ttf") format("opentype"); + font-display: swap; +} +@font-face { + font-family: "PxPlus IBM EGA 8x14"; + src: + local("PxPlus IBM EGA 8x14"), + url("./PxPlus_IBM_EGA_8x14.woff2") format("woff2"), + url("./PxPlus_IBM_EGA_8x14.woff") format("woff"), + url("./PxPlus_IBM_EGA_8x14.ttf") format("opentype"); + font-display: swap; +} +@font-face { + font-family: "Px437 DOS/V re. JPN12"; + src: + local("Px437 DOS/V re. JPN12"), + url("./Px437_DOS-V_re_JPN12.woff2") format("woff2"), + url("./Px437_DOS-V_re_JPN12.woff") format("woff"), + url("./Px437_DOS-V_re_JPN12.ttf") format("opentype"); + font-display: swap; +} diff --git a/src/lib/index.ts b/src/lib/index.ts new file mode 100644 index 0000000..13c5889 --- /dev/null +++ b/src/lib/index.ts @@ -0,0 +1,10 @@ +/* + Copyright (C) 2024 memdmp + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ +export * as styles from "./styles.ts"; diff --git a/src/lib/license.ts b/src/lib/license.ts new file mode 100644 index 0000000..d9c8ffe --- /dev/null +++ b/src/lib/license.ts @@ -0,0 +1,7 @@ +import * as license from "$/../LICENSE?raw"; + +export default (typeof license === "string" + ? license + : "default" in license + ? license.default + : JSON.stringify(license, null, 2)) as string; diff --git a/src/lib/styles.ts b/src/lib/styles.ts new file mode 100644 index 0000000..fd361b4 --- /dev/null +++ b/src/lib/styles.ts @@ -0,0 +1,10 @@ +/* + Copyright (C) 2024 memdmp + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ +export const button = "bg-white bg-opacity-10 text-white p-4 rounded-xl"; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..53bd941 --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,11 @@ + + +
+ {@render children()} +
diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts new file mode 100644 index 0000000..2c01569 --- /dev/null +++ b/src/routes/+layout.ts @@ -0,0 +1,11 @@ +/* + Copyright (C) 2024 memdmp + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ +export const prerender = true; +export const trailingSlash = 'always'; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..701132b --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,414 @@ + + + + + mem.estrogen.zone + + +{#snippet tagLeft(colour: string)} + * +{/snippet} +{#snippet okTagLeft()} + {@render tagLeft("#51f051")} +{/snippet} +{#snippet tagRight(innerText: string, innerColour: string)} + [ + {innerText} + ] +{/snippet} +{#snippet okTagRight()} + {@render tagRight("ok", "#51f051")} +{/snippet} +{#snippet okLine(text: string, classes?: string, rightClass?: string)} +
+
+ {@render okTagLeft()} + {text} +
+
+ {@render okTagRight()} +
+
+{/snippet} +{#snippet ttyTextInnerRenderer(section: RenderBlock)} + {#if section.raw}{@html section.value}{:else}{#each section.value + .split("\n") + .map( (l, i, a) => (i === a.length - 1 ? [0, l] : [1, l]), ) as [nl, l]}{l}{#if nl}
{/if}{/each}{/if}
+{/snippet} +{#snippet ttyTextMiddleRenderer( + section: RenderBlock, + line: TTYText & { kind: "text" }, +)} + {#if section.url} + {#if typeof section.url === "string"} + {#if section.url.startsWith("newtab:")} + {@render ttyTextInnerRenderer(section)} + {:else if section.url.startsWith("currenttab:")} + {@render ttyTextInnerRenderer(section)} + {:else} + ERR: Unknown Link Format + {/if} + {:else} + + typeof section.url !== "function" ? void 0 : section.url(line)} + on:keypress={() => + typeof section.url !== "function" ? void 0 : section.url(line)} + role="link" + tabindex="0">{@render ttyTextInnerRenderer(section)} + {/if} + {:else} + {@render ttyTextInnerRenderer(section)} + {/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"} + + {:else} + {#each line.value as v}{@render ttyTextMiddleRenderer( + v, + line, + )}{/each} + {/if} + {/if} + {/if} +{/snippet} + + + +
+
+
+
+

+ BdsDxe: loading Boot0002 "UEFI Misc Device" from PciRoot + (0x0)/Pci(0x2,0x4)/Pci(0x0,0x0)
+ BdsDxe: starting Boot0002 "UEFI Misc Device" from PciRoot + (0x0)/Pci(0x2,0x4)/Pci(0x0,0x0) +

+
+
+
+ Bootloader Logo +
+
+
Start boot option
+
+ + +`)}");background-size:contain;`} + >
+
+
+
+
+

+ GNU GRUB  version 2.12 +

+
+
+ *Alpine Linux {versions.alpine.number}, with {versions.kernel + .humanReadable} +
+
+  UEFI Firmware Setup +
+
+ +
+
+
+

+   Booting `Alpine Linux {versions.alpine.number}, with {versions + .kernel.humanReadable}'

Loading {versions.kernel.humanReadable} ...
Loading initial ramdisk ... +

+
+ +
+
+
+
+   OpenRC + {versions.openrc} + is starting up Linux + {versions.kernel.id} ({architecture}) +
+
+ {@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", + )} + {@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", + )} + {@render okLine("Checking local filesystems ...", "openrc-boot-step-10")} +
+ /dev/mapper/bepis: clean, {fsckFileCount}/{fsckTotalCount} files, {blocksCount}/{blocksTotal} + blocks +
+ {@render okLine( + "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( + "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")} +
udhcpd: started
+
udhcpd: broadcasting discover
+ +
+ udhcpd: broadcasting select for 192.168.1.{lanLeastSignificantIPPart}, + server 192.168.1.1 +
+
+ udhcpd: lease of 192.168.1.{lanLeastSignificantIPPart} obtained from 192.168.1.1, + lease time 3600 +
+ {@render okLine( + "Seeding random number generator ...", + "openrc-boot-step-26", + )} + {@render okLine( + "Seeding 256 bits without crediting", + "openrc-boot-step-26", + )} + {@render okLine( + "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")} +
+
+ Welcome to Alpine Linux {versions.alpine + .number}{#if versions.alpine.isEdge} (edge){/if} +
+
+ Kernel {versions.kernel.humanReadable} on an {architecture} (/dev/{tty}) +
+
+
+ {hostname} login: {#each login.username + .split("") + .map((v, i) => [v, i] as const) as [char, idx]}{char}{/each}
+ Password: _
+
+ +
+ +
_
+
+
+
+ {#each ttyLines.filter(((maxidx) => (_, i) => i < (maxidx === -1 ? Infinity : maxidx))(ttyLines.findIndex((v) => v.kind === "clear"))) as line} + {@render ttyText(line)} + {/each} +
+
+
+ {#if ttyLines.find((v) => v.kind === "clear")} + {#each ttyLines + .map((v, i) => [v, i] as const) + .filter(([v]) => v.kind === "clear") + .map((v, i, a) => [v[1], a[i + 1]?.[1], i]) as [idx, nextIdx, clearIdx]} +
+
+
+ {#each ttyLines.filter((_, i) => i >= idx && i < (nextIdx ?? Infinity)) as line} + {@render ttyText(line)} + {/each} +
+
+ {/each} + {/if} +
+ + diff --git a/src/routes/anim-gen.ts b/src/routes/anim-gen.ts new file mode 100644 index 0000000..9138ca9 --- /dev/null +++ b/src/routes/anim-gen.ts @@ -0,0 +1,504 @@ +/* + Copyright (C) 2024 memdmp + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ +import { Animation, MultiObjectKeyframe } from '@memdmp/keyframegen'; +import { + biosStepInterval, + biosSteps, + getDelay, + login, + ttyLines, +} from './shared.ts'; +const anim = new Animation(); +let ttyCtr = 0; +const stages = [ + anim.selector('#bios'), + anim.selector('#grub'), + anim.selector('#grub-term'), + anim.selector('#openrc'), + ...(ttyLines.flatMap((v) => + v.kind === 'clear' + ? [ + anim.selector('#tty-' + ttyCtr++), + ] + : [] + )), +]; +const visibleStageStyles = `margin-top: 0; +margin-bottom: 0; +margin-left: 0; +margin-right: 0; +height: 100vh; +width: 100vw; +opacity: 1;`; +const hiddenStageStyles = `margin-top: -99999vh; +margin-bottom: 99999vh; +margin-left: -99999vw; +margin-right: 99999vw; +height: 0px; +width: 0px; +opacity: 0;`; +const visibleStyles = `margin-top: 0; +margin-bottom: 0; +margin-left: 0; +margin-right: 0; +transform: none; +opacity: 1;`; +const hiddenStyles = `margin-top: -99999vh; +margin-bottom: 99999vh; +margin-left: -99999vw; +margin-right: 99999vw; +transform: scaleX(0); +opacity: 0;`; +const altHiddenStyles = `opacity:0; +transform:scaleX(0); +width:0; +height:0; +margin-left:-100vw; +margin-right:100vw;`; +const altVisibleStyles = `opacity:1; +transform:none; +width:max-content; +height:max-content; +margin-left:0; +margin-right:0;`; +stages.forEach((v, i) => + v.style(i === 0 ? visibleStageStyles : hiddenStageStyles) +); +let currentStage: typeof stages[number]; +const toStage = (stage: number) => { + const oldStage = currentStage; + currentStage = stages[stage]; + if (typeof oldStage !== 'undefined') { + oldStage.style( + visibleStageStyles, + ); + currentStage.style( + hiddenStageStyles, + ); + anim._internal_timeline.now += 1; + oldStage.style( + hiddenStageStyles, + ); + } + currentStage.style( + visibleStageStyles, + ); +}; + +type Step = (next: () => void) => void; + +const biosStepHandlers: Step[] = [ + // Show BIOS + (next) => { + toStage(0); + anim.in(1000 * 3 / 10, next); + }, + // Show Start boot option + (next) => { + const s = anim.selector('#bios .start-text'); + s.style(hiddenStyles); + anim._internal_timeline.now += 1; + s.style(visibleStyles); + anim.in(1000, next); + }, + // Show bar parts + ...new Array( + biosSteps, + ).fill( + (quantity: number, lastQuantity: number, index: number) => + (next: () => void) => { + const s = anim.selector( + `#bios .bar`, + ); + s.style(`${index === 0 ? hiddenStyles : visibleStyles} +width: ${lastQuantity * 100}vw;`); + anim._internal_timeline.now += 1; + s.style(`${visibleStyles} +width: ${quantity * 100}vw;`); + anim.in(quantity === 1 ? 50 : biosStepInterval, next); + }, + ).map((v, i, a) => v((i + 1) / a.length, i / a.length, i)), + // Show bdsdxe + (next) => { + const s1 = anim.selector('#bios .bdsdxe-load'); + const s2 = anim.selector('#bios .bdsdxe-start'); + s1.style(hiddenStyles); + s2.style(hiddenStyles); + anim._internal_timeline.now += 1; + s1.style(visibleStyles); + anim.in(75, () => { + s2.style(visibleStyles); + anim.in(50, next); + }); + }, +]; +const grubStepHandlers: Step[] = [ // Show Grub + (next) => { + toStage(1); + anim.in(1000, next); + }, + // Hide 2s, show 1s + (next) => { + const g2s = anim.selector('#grub .grub-2s'); + const g1s = anim.selector('#grub .grub-1s'); + g2s.style('width:max-content;opacity:1;'); + g1s.style('width:0;opacity:0;'); + anim._internal_timeline.now += 1; + g2s.style('width:0;opacity:0;'); + g1s.style('width:max-content;opacity:1;'); + anim.in(1000, next); + }, + // Show Booting Alpine Grub + (next) => { + toStage(2); + anim.in(100, next); + }, + (next) => { + const s = anim.selector('#grub-term .load-kernel'); + s.style(hiddenStyles); + anim._internal_timeline.now += 1; + s.style(visibleStyles); + anim.in(200, next); + }, + (next) => { + const s = anim.selector('#grub-term .load-ramdisk'); + s.style(hiddenStyles); + anim._internal_timeline.now += 1; + s.style(visibleStyles); + anim.in(950, next); + }, +]; +const typingIndicatorFlashTime = 200; +const typingIndicatorHidden = ` +opacity: 0; +transform: scaleX(0); +`; +const typingIndicatorVisible = ` +opacity: 1; +transform: none; +`; +/** Returns the last state it was in */ +const idleTypingBar = ( + typingBarSelector: MultiObjectKeyframe, + idleFor: number, +) => { + let flashingTimeCtr = 0; + let isHiddenInCycle = false; + typingBarSelector.style(typingIndicatorVisible); + const v = () => + isHiddenInCycle ? typingIndicatorHidden : typingIndicatorVisible; + while ((flashingTimeCtr += typingIndicatorFlashTime) < idleFor) { + if (flashingTimeCtr === typingIndicatorFlashTime) flashingTimeCtr /= 2; + anim.in(flashingTimeCtr, () => { + isHiddenInCycle = !isHiddenInCycle; + typingBarSelector.style( + v(), + ); + }); + anim.in(flashingTimeCtr - 1, () => { + typingBarSelector.style( + v(), + ); + }); + } + return v(); +}; +const typeCharacter = ( + typingBarSelector: MultiObjectKeyframe, + characterSelector: MultiObjectKeyframe | undefined | null, + next: () => void, + characterTime: number, +) => { + const characterHidden = ` + transform:scaleX(0); + width: 0; + opacity: 0; + `; + const characterVisible = ` + transform:none; + width: max-content; + opacity: 1; + `; + typingBarSelector.style(typingIndicatorVisible); + if (characterSelector) { + characterSelector.style(characterHidden); + anim._internal_timeline.now += 1; + characterSelector.style(characterVisible); + } + if (characterTime > typingIndicatorFlashTime) { + const res = idleTypingBar(typingBarSelector, characterTime); + anim.in(characterTime - 1, () => { + typingBarSelector.style(res); + }); + anim.in(characterTime, () => { + typingBarSelector.style(typingIndicatorHidden); + }); + anim.in(characterTime + 1000 / 60, next); + } else { + anim.in(characterTime, () => { + typingBarSelector.style(typingIndicatorHidden); + }); + anim.in(characterTime + 1000 / 60, next); + } +}; +const openrcStepHandlers = (multi: number): Step[] => [ + // Show OpenRC + (next) => { + toStage(3); + next(); + }, + ...[ + // Steps 1-3 + 1000 / 60, + 200, + 1000 / 30, + // Steps 4-9 + 1000 / 60, + 1000 / 60, + 4000 / 60, + 1000 / 40, + 400, + 75, + // Steps 10-15 + 150, + 100, + 85, + 100, + 3500 / 60, + 125, + // Steps 16-20 + 3500 / 60, + 100, + 150, + 1000 / 60, + 1000 / 60, + 1000 / 60, + // Steps 21-25 + 1000 / 85, + 1000 / 45, + 1000 / 60, + 1000 / 60, + 500, + // Steps 26-30 + 750, + 400, + 100, + 100, + 400, + // Time till typing: + 1000, + ].map((v) => v / multi).map((time, idx, arr) => (next: () => void) => { + const s = anim.selector('#openrc .openrc-boot-step-' + idx); + s.style(`${hiddenStyles} +max-height: 0;`); + if (idx === arr.length - 1) { + anim.selector('#openrc .openrc-hide-at-last-boot-step').style( + 'opacity:1;max-height:90vh;max-width:100vw;', + ); + } + anim._internal_timeline.now += 1; + if (idx === arr.length - 1) { + anim.selector('#openrc .openrc-hide-at-last-boot-step').style( + 'opacity:0;max-height:0;max-width:0;', + ); + } + s.style(`${visibleStyles} +max-height: max-content;`); + if (idx === arr.length - 1) { + idleTypingBar(anim.selector('#openrc .openrc-username-anim'), time); + } + anim.in(time, next); + }), + // Typing Username + ...((() => { + const typingBar = anim.selector('#openrc .openrc-username-anim'); + return login.username.split('').map((_, i) => { + const character = anim.selector( + `#openrc .openrc-username .openrc-username-char.openrc-username-char-${i}`, + ); + return (next: () => void) => + typeCharacter(typingBar, character, next, getDelay(login)); + }); + })()), + // Hide Username Cursor + (next) => { + const s = anim.selector( + '#openrc .openrc-hide-at-login-prompt-username-done', + ); + s.style(`transform:none;width:max-content;opacity:1;`); + anim._internal_timeline.now += 1; + s.style(`transform:scaleX(0);width:0;opacity:0;`); + next(); + }, + // Show Password + (next) => { + const s = anim.selector('#openrc .openrc-pw-line'); + s.style(`transform:scaleX(0);width:0;opacity:0;`); + anim._internal_timeline.now += 1; + s.style(`transform:none;width:max-content;opacity:1;`); + next(); + }, + // Password Typing + ...(new Array(login.passwordLength).fill( + (_idx: number) => (next: () => void) => { + const s = anim.selector('#openrc .openrc-password-anim'); + const characterTime = getDelay(login); + typeCharacter(s, null, next, characterTime); + }, + ).map((v, i) => v(i))), + (next) => { + const s = anim.selector('#openrc .openrc-password-anim'); + s.style('opacity:0;transform:scaleX(0);'); + anim.in(1, next); + }, +]; +const ttyStepHandlers: Step[] = [ + (next) => anim.in(100, next), + (next) => { + const s = anim.selector( + `#openrc .ttylines-openrc`, + ); + s.style(altHiddenStyles); + anim.in(1, () => { + s.style(altVisibleStyles); + next(); + }); + }, + ...(() => { + let isBeforeFirstClear = true; + let ttyIdx = 0; + return ttyLines.map((step) => (next: () => void) => { + switch (step.kind) { + case 'text': { + const s = anim.selector( + `${isBeforeFirstClear ? '#openrc' : '#tty-' + (ttyIdx - 1)} ${ + step.classes.map((v) => `.${v}`).join('') + }`, + ); + s.style(altHiddenStyles); + anim.in(1, () => { + s.style(altVisibleStyles); + next(); + }); + break; + } + case 'removeNode': { + const s = anim.selector( + `${isBeforeFirstClear ? '#openrc' : '#tty-' + (ttyIdx - 1)} ${ + step.removedItemClassList.map((v) => `.${v}`).join('') + }`, + ); + s.style(altVisibleStyles); + anim.in(1, () => { + s.style(altHiddenStyles); + next(); + }); + break; + } + case 'time': { + anim.in(step.delay, next); + break; + } + case 'cursorVisibility': { + next(); + break; + } + case 'clear': { + if (isBeforeFirstClear) { + isBeforeFirstClear = false; + } + toStage(3 + (++ttyIdx)); + next(); + break; + } + } + }); + })(), +]; + +const handleSteps: Step[] = [ + // (n) => { + // toStage(0); + // anim.in(500, n); + // }, + ...biosStepHandlers, + ...grubStepHandlers, + ...openrcStepHandlers(1), + ...ttyStepHandlers, + (n) => { + const s = anim.selector('#app .hidden-after-anim'); + s.style(visibleStyles); + anim._internal_timeline.now += 1; + s.style(hiddenStyles); + anim.in(1000, n); + }, +]; +const nextStep = () => handleSteps.shift()!(nextStep); +handleSteps.push(() => { + anim.style('#nonexistentelement', ''); + // we done +}); +nextStep(); +const output = `/** + * @generated + * @license AGPL-3.0-OR-LATER + * @copyright 2024 memdmp + * + * Copyright (C) 2024 memdmp + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this program. If not, see . + */ +${anim.exportToCSS()} +${ + stages.filter((_, i, a) => i !== a.length - 1).map((v) => + `${v.selector} { +${hiddenStageStyles} +} +.ttytext-removed-after-done { + display: inline-block; + opacity: 0; + width: 0; + height: 0; + margin-left: -100vw; + margin-right: 100vw; +} +${ + (ttyLines.flatMap((v) => + v.kind === 'text' + ? [ + ...v.value.map((v) => ({ + kind: 'text' as const, + ...v, + })), + ] + : [] + ).flatMap((v) => [ + ...( + v.kind === 'text' + ? [ + v.colour + ? `.ttytext-block.text-\\[\\${v.colour}\\]{color:${v.colour};}` + : '', + v.bg + ? `.ttytext-block.bg-\\[\\${v.bg}\\]{background:${v.bg};}` + : '', + ] + : [] + ), + ]).filter((v, i, a) => v.length !== 0 && a.indexOf(v) === i)).join('\n') + } +` + ).join('') +}`; +console.log(output); diff --git a/src/routes/boot-logo.png b/src/routes/boot-logo.png new file mode 100644 index 0000000..fa3edc0 Binary files /dev/null and b/src/routes/boot-logo.png differ diff --git a/src/routes/canaries/+page.svelte b/src/routes/canaries/+page.svelte new file mode 100644 index 0000000..adbbdcc --- /dev/null +++ b/src/routes/canaries/+page.svelte @@ -0,0 +1,5 @@ + + +{@debug keyStore} diff --git a/src/routes/canaries/canaries.ts b/src/routes/canaries/canaries.ts new file mode 100644 index 0000000..de5c44f --- /dev/null +++ b/src/routes/canaries/canaries.ts @@ -0,0 +1,25 @@ +export const canaries: Canary[] = []; +export class Canary { + public constructor( + public name: string, + public description: string, + public url: string, + public keyIdentifier: string, + ) { + canaries.push(this); + } +} +new Canary( + 'estrogen.zone, memdmp', + 'This canary is responsible for services hosted around estrogen.zone and yuridick.gay', + '/canaries/memdmp:estrogen.zone', + 'memdmp', +); + +new Canary( + 'kyun.host', + 'This canary is responsible for the VPS provider "kyun.host"', + // '/canaries/napatha:kyun.host', + 'https://files.kyun.host/canary.txt', + 'napatha', +); diff --git a/src/routes/canaries/keystore.ts b/src/routes/canaries/keystore.ts new file mode 100644 index 0000000..788428d --- /dev/null +++ b/src/routes/canaries/keystore.ts @@ -0,0 +1,120 @@ +import { PublicKey, readCleartextMessage, readKey, verify } from 'openpgp'; +export const keyStore = new Map(); +export const validateSignature = async ( + message: string, + id: string, +) => { + id = id.toUpperCase(); + const key = keyStore.get(id) ?? keyStore.get(id.replace(/ /g, '')); + if (!key) throw new Error('Could not find key from keystore'); + const signedMessage = await readCleartextMessage({ + cleartextMessage: message, + }); + const verificationResult = await verify({ + message: signedMessage, + verificationKeys: key, + expectSigned: true, + }); + return verificationResult.data; +}; +const pushKey = async ( + { ids, key, is_url, expectUserIds, signed_by }: { + ids?: string[]; + expectUserIds?: string[]; + key: string; + is_url?: boolean; + signed_by?: string; + }, +) => { + ids = ids ?? []; + if (is_url) { + key = await fetch( + new URL(key, 'https://keys.openpgp.org/vks/v1/by-fingerprint/'), + {}, + ).then((v) => v.text()); + } + if (signed_by) { + key = await validateSignature(key, signed_by); + } + const parsedKey = await readKey({ + armoredKey: key, + }).then((v) => v.toPublic()); + { + const missingUserIds = + expectUserIds?.filter((v) => !expectUserIds.includes(v)) ?? []; + if (missingUserIds.length) { + throw new Error( + `Key ${parsedKey.getFingerprint()} is missing User IDs: ${ + missingUserIds.join(', ') + }`, + ); + } + } + ids.push( + parsedKey.getKeyID().toHex().replace(/ /g, ''), + parsedKey.getFingerprint().replace(/ /g, ''), + ...(expectUserIds ?? []), + ); + ids = ids.filter((v, i, a) => a.indexOf(v) === i).map((v) => v.toUpperCase()); + for (const id of ids) { + keyStore.set(id, parsedKey); + } +}; +await pushKey({ + key: 'B546778F06BBCC8EC167DB3CD919706487B8B6DE', + ids: ['memdmp'], + expectUserIds: [ + 'memdmp ', + 'memdmp ', + ], + is_url: true, +}); +await pushKey({ + // TODO: when primary memdmp key rotates, or when this key expires, replace this inline string with a new one + key: `-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +- -----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: User ID: memdmp canary keysig +Comment: Valid from: 22 Nov 2024 18:31:11 +Comment: Valid until: 22 Nov 2027 12:00:00 +Comment: Type: 255-bit EdDSA (secret key available) +Comment: Usage: Signing, Encryption, Certifying User IDs +Comment: Fingerprint: 55D3582CAE78601990A8CA1DBFD0F9E61CB7D84E + +mDMEZ0C/3xYJKwYBBAHaRw8BAQdA5w4ET7V3FmasUc3h9sb0O0/y38LXp+IUV8Wf +La95jm20ZG1lbWRtcCBjYW5hcnkga2V5c2lnIDxtZW1kbXAta2V5LWZvci1zaWdu +aW5nLWNhbmFyeS1yZWxhdGVkLWtleXMtZm9yLWV4dGVybmFsLXNlcnZpY2VzQGZh +a2VtYWlsLnV3dT6ImQQTFgoAQRYhBFXTWCyueGAZkKjKHb/Q+eYct9hOBQJnQL/f +AhsDBQkFoz7RBQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAAAoJEL/Q+eYct9hO +X68BAPPBy76J7EWb25+fj/QUD0rYyi/E2kLfGbW+PLhrB/AdAQDl5icCilAI/2xv +X4jpGCH9KdJoClIV4g2AyKoEITKBDbg4BGdAv98SCisGAQQBl1UBBQEBB0CcYmml +AWFCXVjIerJJrs/GA65EZDwoZowiVVTS99FvaQMBCAeIfgQYFgoAJhYhBFXTWCyu +eGAZkKjKHb/Q+eYct9hOBQJnQL/fAhsMBQkFoz7RAAoJEL/Q+eYct9hOr2IA/22U +2rOPevvUoiObv/DeeQlP2mvaQcOCFHp1HVF+4oHrAQDWZiihBvdIESbqm5MH0zLe +EkEE03+lW4Zbe25P6MHsBg== +=5NPo +- -----END PGP PUBLIC KEY BLOCK----- +-----BEGIN PGP SIGNATURE----- + +iIoEARYKADIWIQS1RnePBrvMjsFn2zzZGXBkh7i23gUCZ0DABRQcbWVtZG1wQG1l +bWV3YXJlLm5ldAAKCRDZGXBkh7i23vV5AP9K2Q6j6cOGovTVqsWlThK7qxA2Faz+ +ZQ4KTbprMz8J4AD/bG33f9Kqg3AqehEyU2TldJs9U9Oni5AXGSGfKLJhmQc= +=945T +-----END PGP SIGNATURE----- +`, + signed_by: 'memdmp ', + ids: ['canary-sigkey-signing'], +}); +await pushKey({ + // TODO: adapt to the relevant url on current domain when up + key: 'https://files.catbox.moe/yf4x40.sig', + ids: ['napatha'], + expectUserIds: [ + 'chef naphtha ', + ], + is_url: true, + signed_by: 'canary-sigkey-signing', +}); + +export default keyStore; diff --git a/src/routes/canaries/napatha:kyun.host/+server.ts b/src/routes/canaries/napatha:kyun.host/+server.ts new file mode 100644 index 0000000..260d15e --- /dev/null +++ b/src/routes/canaries/napatha:kyun.host/+server.ts @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export const GET = () => { + throw redirect(307, 'https://files.kyun.host/canary.txt'); +}; diff --git a/src/routes/distro-info.ts b/src/routes/distro-info.ts new file mode 100644 index 0000000..0c4656e --- /dev/null +++ b/src/routes/distro-info.ts @@ -0,0 +1,38 @@ +/* + Copyright (C) 2024 memdmp + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ +export const architecture = 'x86_64'; +export const versions = { + alpine: { + number: '3.21.0', + isEdge: true, + }, + kernel: { + id: '6.6.53-0-lts', + humanReadable: 'Linux lts', + }, + openrc: '0.55.1.ba16daf355', + udhcpd: '1.36.1', +}; +export const tty = 'tty1'; +export const hostname = 'estrogen.zone'; +export const interfaces = { + // key: iface name + // value: time for it coming up, in millis + lo: 0, + eth0: Math.random() * 128 + 2, +}; + +if (versions.alpine.isEdge) { + versions.alpine.number += `_alpha${ + new Date().getUTCFullYear().toString() + + new Date().getUTCMonth().toString().padStart(2, '0') + + new Date().getUTCDate().toString().padStart(2, '0') + }`; +} diff --git a/src/routes/license/+server.ts b/src/routes/license/+server.ts new file mode 100644 index 0000000..14db510 --- /dev/null +++ b/src/routes/license/+server.ts @@ -0,0 +1,5 @@ +import license from "$lib/license.ts"; + +export const GET = () => { + return new Response(license); +}; diff --git a/src/routes/shared.ts b/src/routes/shared.ts new file mode 100644 index 0000000..a5ff683 --- /dev/null +++ b/src/routes/shared.ts @@ -0,0 +1,485 @@ +/* + Copyright (C) 2024 memdmp + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . +*/ +export const biosSteps = 3; +export const biosStepInterval = 1000; + +const wpmToTypingSpeed = (wpm: number, avgWordLen: number) => + (1 / (wpm * avgWordLen / 60)) * 1000; + +const averageWordLength = 4.793; +const typingInfo = (wpm = 80) => ({ + typingSpeedAvg: wpmToTypingSpeed(wpm, averageWordLength), + typingDeviation: 20, // typing speed deviation is often correlated to the typing speed - TODO: at way lower speeds, the correlation is inverse; the more deviation there is (is this also the case for fast typing? what is the inflection point if not? i forgor i went down a rabbit hole yrs ago) +}); +export type TypingSpeed = ReturnType; +export const getDelay = (typingInfo: TypingSpeed) => + (Math.random() * 2 - 1) * typingInfo.typingDeviation + + typingInfo.typingSpeedAvg; +export const login = { + username: 'lain', + passwordLength: 12, + ...typingInfo(100), // one usually has muscle memory for well-known frequently-typed credentials, hence higher wpm +}; +export const ttyTypingInfo = typingInfo(); + +export type RenderBlock = { + value: string; + colour?: string; + weight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900; + italic?: boolean; + underlined?: boolean; + url?: + | `newtab:${string}` + | `currenttab:${string}` + | ((textObj: TTYText & { kind: 'text' }) => void); + bg?: string; + raw?: boolean; + dl?: string; +}; +export type TTYText = { + kind: 'text'; + renderrestriction?: 'everywhere' | 'js-only' | 'noscript'; + value: RenderBlock[]; + id: string; + classes: string[]; +} | { + kind: 'removeNode'; + removedId: string; + removedItemClassList: string[]; +} | { + kind: 'time'; + delay: number; +} | { + kind: 'cursorVisibility'; + visible: boolean; +} | { + kind: 'clear'; +}; +export type Only = { + [k in Keys]: Obj[k]; +}; +export type Diff< + T extends { + [k: string]: unknown; + }, + U extends keyof T, +> = ({ [P in keyof T]: P } & { [P in U]: never } & { [x: string]: never })[ + keyof T +]; +export type Omit< + T extends { + [k: string]: unknown; + }, + K extends keyof T, +> = Pick< + T, + Diff +>; +export const ttyLines: TTYText[] = (() => { + const lines: TTYText[] = []; + let ids: string[] = []; + const byId = new Map(); + let i = 69420; + let defaultRenderRestriction: Only< + TTYText & { kind: 'text' }, + 'renderrestriction' + >['renderrestriction'] = 'everywhere'; + type LimitedRenderBlock = Omit; + 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 + | RenderBlockArg, + ...blocks: RenderBlockArg[] + ) => { + const id = (++i).toString(); + ids.push(id); + if (Array.isArray(globalOptions)) { + blocks.unshift(globalOptions); + globalOptions = {}; + } + const txt = { + kind: 'text', + renderrestriction: globalOptions.renderrestriction ?? + defaultRenderRestriction, + value: blocks.map(([t, o]) => ({ + value: t, + colour: '#b9b9b9', + ...o, + })), + id, + classes: [`ttytext-${id}`], + } as TTYText; + lines.push(txt); + byId.set(id, txt); + return id; + }; + const wait = (time: number) => + lines.push({ + kind: 'time', + delay: time, + }); + const del = (id: string | number) => { + const removedId = typeof id === 'string' + ? id + : id >= 0 + ? ids[id] + : ids[ids.length + id]; + const r = byId.get(removedId); + if (r?.kind === 'text') { + lines.push({ + kind: 'removeNode', + removedId, + removedItemClassList: r.classes, + }); + 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]; + 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'); + lines.push({ + kind: 'removeNode', + removedId, + removedItemClassList: r.classes, + }); + } + } + ids = ids.filter((_, i) => i < idIndex); + }; + const getLast = () => { + const v = byId.get(ids[ids.length - 1]); + 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'; + del(-1); + return text({ + renderrestriction: l, + }, ...newValue); + }; + const replaceLast = ( + handler: (newValue: RenderBlockArg[]) => RenderBlockArg[], + ) => + overwriteLast( + ...handler( + getLast()!.map( + (v) => + [ + v.value, + Object.fromEntries( + Object.entries(v).filter(([k]) => k !== 'value'), + ), + ] as const, + ), + ), + ); + const everyTTYClear: (() => void)[] = []; + const clear = () => { + if (lines.find((v) => v.kind === 'clear')) { + delSince(lines.findLastIndex((v) => v.kind === 'clear')); + } + lines.push({ kind: 'clear' }); + everyTTYClear.forEach((v) => v()); + }; + + everyTTYClear.push(() => { + text({ + renderrestriction: 'noscript', + }, ['This browser does not support JS. Your experience may be degraded.', { + bg: '#ff0000', + colour: '#dedede', + }], ['\n', {}]); + }); + everyTTYClear.forEach((v) => v()); + + // + + wait(300); + 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', + 'overhead: "I don\'t consent" "hey thats my line"', + 'uwu', + 'i regret making this hellhole of a codebase', + ]), + { + colour: '#777777', + }, + ]); + wait(1000); + clear(); + text( + ['lain', { + colour: '#FFFF53', + weight: 700, + }], + [' in '], + ['mem.estrogen.zone', { + colour: '#17B117', + weight: 700, + }], + [' in '], + ['~', { + colour: '#53FFFF', + weight: 700, + }], + ); + text( + ['❯ ', { colour: '#53FF53', weight: 600 }], + [''], + ['', { colour: '#777777' }], + ); + { + 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', + ]; + for (const c of [...targetstr.split(''), 'COMPLETE']) { + replaceLast((l) => { + const prompt = l[l.length - 2]; + const suggestions = l[l.length - 1]; + if (c === 'COMPLETE') { + prompt[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] = ''; + } + return l; + }); + if (c === 'COMPLETE') { + wait(100); + } else wait(50 + Math.random() * 100); + } + text(['Preparing...']); + wait(200); + } + wait(1000); + clear(); + text( + ['Welcome to'], + [''], + [' the Estrogen Zone!\n\n'], + ); + wait(300); + for (const c of ' my corner of'.split('')) { + replaceLast((l) => { + l[1][0] += c; + return l; + }); + wait(70); + } + text( + ['Places you can find me:', { + colour: '#7a7a7a', + }], + ); + wait(300); + text( + [' - ', { + colour: '#7a7a7a', + }], + ['Estradiol SourceHut: ', { colour: '#cdcdcd' }], + ); + wait(100); + replaceLast((v) => [...v, ['sh.estrogen.zone/~memdmp', { + colour: '#99aaff', + underlined: true, + weight: 700, + url: 'newtab:https://sh.estrogen.zone/~memdmp', + }]]); + wait(100); + text( + [' - ', { + colour: '#7a7a7a', + }], + ['On jsr: ', { colour: '#cdcdcd' }], + ); + wait(600); + replaceLast((v) => [...v, ['jsr.io/@memdmp', { + colour: '#f7df23', + underlined: true, + weight: 700, + url: 'newtab:https://sh.estrogen.zone/~memdmp', + }]]); + wait(200); + text( + [' - ', { + colour: '#7a7a7a', + }], + ['In random hackerspaces', { colour: '#aaaaaa' }], + ); + wait(600); + text( + ['\nYou may find these useful:', { + colour: '#7a7a7a', + }], + ); + wait(400); + text( + [' - ', { + colour: '#7a7a7a', + }], + ['GPG Root Key: ', { colour: '#cdcdcd' }], + ); + wait(1000); + replaceLast((v) => [...v, ['B546778F06BBCC8EC167DB3CD919706487B8B6DE', { + colour: '#58C7F3', + underlined: true, + weight: 700, + url: 'currenttab:/keys/memdmp/primary.pgp', + dl: 'memdmp-primary.pgp', + }]]); + wait(100); + replaceLast((v) => [...v, [' (root key)', { + colour: '#999999', + }]]); + wait(400); + text( + [' - ', { + colour: '#7a7a7a', + }], + ); + wait(70); + replaceLast( + (v) => [...v, ['GPG Git Key: ', { colour: '#cdcdcd' }]], + ); + wait(100); + replaceLast((v) => [...v, ['5134F05BD8D9DB8C6C0E1515A9439D346AB6DF4E', { + colour: '#F0A3B3', + underlined: true, + weight: 700, + url: 'currenttab:/keys/memdmp/git.pgp', + dl: 'memdmp-git.pgp', + }]]); + wait(100); + replaceLast((v) => [...v, [' (', { + colour: '#999999', + }], ['signed', { + colour: '#F0A3B3', + underlined: true, + weight: 500, + url: 'currenttab:/keys/memdmp/git.pgp.sig', + dl: 'memdmp-git.pgp.sig', + }], [')', { colour: '#999999' }]]); + wait(45); + text( + [' - ', { + colour: '#7a7a7a', + }], + ); + wait(70); + replaceLast( + ( + v, + ) => [...v, ['GPG Release Signing Key: ', { + colour: '#cdcdcd', + }]], + ); + wait(100); + replaceLast((v) => [...v, ['0D93102265071798C7B65A4C9F0739B9E0C8FD60', { + colour: '#ffffff', + underlined: true, + weight: 700, + url: 'currenttab:/keys/memdmp/release.pgp', + dl: 'memdmp-release.pgp', + }]]); + wait(100); + replaceLast((v) => [...v, [' (', { + colour: '#999999', + }], ['signed', { + colour: '#ffffff', + underlined: true, + weight: 500, + url: 'currenttab:/keys/memdmp/release.pgp.sig', + dl: 'memdmp-release.pgp.sig', + }], [')', { colour: '#999999' }]]); + wait(100); + text( + [' - ', { + colour: '#7a7a7a', + }], + ); + wait(35); + replaceLast( + ( + v, + ) => [...v, ['Source Code: ', { + colour: '#cdcdcd', + }]], + ); + wait(100); + replaceLast((v) => [...v, ['git.sh.estrogen.zone/~memdmp/mem.estrogen.zone', { + colour: '#F0A3B3', + underlined: true, + weight: 700, + url: 'newtab:/upstream/', + }]]); + wait(100); + text( + [' - ', { + colour: '#7a7a7a', + }], + ); + wait(35); + replaceLast( + ( + v, + ) => [...v, ['Canaries: ', { + colour: '#cdcdcd', + }]], + ); + wait(100); + replaceLast((v) => [...v, ['/canaries/', { + colour: '#58C7F3', + underlined: true, + weight: 700, + url: 'currenttab:/canaries/', + }]]); + wait(5000); + text([ + ``, + { + raw: true, + url: () => alert('i abused too much css for this i wanna cry now'), + }, + ]); + + // + + return lines; +})(); diff --git a/src/routes/upstream/+server.ts b/src/routes/upstream/+server.ts new file mode 100644 index 0000000..678e4cd --- /dev/null +++ b/src/routes/upstream/+server.ts @@ -0,0 +1,6 @@ +import { redirect } from "@sveltejs/kit"; +import { repository } from "$/../package.json" with { type: "json" }; + +export const GET = () => { + throw redirect(307, repository.url); +}; -- cgit v1.2.3