diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.css | 67 | ||||
| -rw-r--r-- | src/lib/blog/Post.svelte | 53 | ||||
| -rw-r--r-- | src/lib/blog/Post.ts | 1 | ||||
| -rw-r--r-- | src/lib/console.ts | 3 | ||||
| -rw-r--r-- | src/lib/storage.ts | 2 | ||||
| m--------- | src/lib/vendor/storage | 0 | ||||
| -rw-r--r-- | src/routes/blog/+page.svelte | 92 | ||||
| -rw-r--r-- | src/routes/blog/[id=int]/+page.svelte | 10 | ||||
| -rw-r--r-- | src/routes/blog/_posts/+page.server.ts | 13 | ||||
| -rw-r--r-- | src/routes/blog/_posts/+page.svelte | 32 | ||||
| -rw-r--r-- | src/routes/blog/base-post.svx | 1 | ||||
| -rw-r--r-- | src/routes/blog/posts/alpine-ssh-early-initfs.svx | 113 | ||||
| -rw-r--r-- | src/routes/blog/posts/test-post.svx | 3 | ||||
| -rw-r--r-- | src/routes/canary/+page.svelte | 4 | ||||
| -rw-r--r-- | src/routes/shared.ts | 29 |
15 files changed, 342 insertions, 81 deletions
diff --git a/src/app.css b/src/app.css index c98a44c..c41d249 100644 --- a/src/app.css +++ b/src/app.css @@ -71,48 +71,67 @@ &:not(:is(p)) { @apply text-white/95; } - &:is(blockquote) { - &::before { - background-color: var(--colour-full); - } - } &::after { @apply -ml-3 bg-gray-200/50; } } + +@utility blockquote-* { + @apply pl-4 p-2 relative my-1; + &::before { + @apply absolute w-1 transition-opacity left-0 top-[10%] h-[80%] opacity-50; + content: ''; + background-color: --value(--color-*); + } + &:hover::before { + @apply opacity-100; + } + p { + &::after { + @apply hidden; + } + } +} +@utility md-card-* { + @apply p-4 rounded-lg bg-neutral-950 border-[1.8px] my-4 sticky top-4 z-50; + border-color: --value(--color-*); +} + #postmd { @apply font-sans; a { - @apply quicklink; + @apply quicklink text-nowrap; } h1 { @apply text-4xl mb-1.5 mt-2; } h2 { - @apply my-1.5 text-2xl; + @apply my-1.5 text-3xl; } h3 { - @apply my-1 text-xl; + @apply my-1 text-2xl; } h4 { - @apply my-1 text-lg; + @apply my-1 text-xl; } - h1,h2,h3,h4,h5,h6,p { + h1,h2,h3,h4,h5,h6,p,blockquote:not(.no-at-a-glance) { @apply relative text-white/90 hover:text-white; &::after { content: ""; @apply absolute top-[20%] left-0 h-[60%] w-0.5 -ml-4 rounded-full transition-all; } - &:is(p)::after { + &:is(p)::after, + &:is(blockquote)::after { @apply bg-gray-200/5; } - &:not(:is(p))::after { + &:not(:is(p)):not(:is(blockquote))::after { @apply bg-gray-200/15; } &:hover::after { @apply -ml-3 bg-gray-200/75; } - &:hover ~ p:is(p)::after { + &:hover ~ p::after, + &:hover ~ blockquote::after { @apply bg-gray-200/4; } } @@ -136,16 +155,17 @@ @apply internal-header-active; } } - h5,h6,p { + h5,h6,p,blockquote:not(.no-at-a-glance) { &:not(:hover):has(~*:hover):not(:has(~h1~*:hover,~h2~*:hover,~h3~*:hover,~h4~*:hover,~h5~*:hover,~h1:hover,~h2:hover,~h3:hover,~h4:hover,~h5:hover)) { @apply internal-header-active; } } - p::after { + p::after, + blockquote:not(.no-at-a-glance)::after { @apply h-[90%] top-[5%]; } p { - @apply my-1; + @apply my-1.5 leading-5; } code { @apply font-genericmono; @@ -195,20 +215,7 @@ } } blockquote:not(.default-blockquote) { - @apply -ml-1 pl-4 p-2 relative my-1; - &::before { - @apply absolute w-1 bg-accent-primary/50 transition-colors left-0 top-[10%] h-[80%]; - content: ''; - --colour-full: var(--color-accent-primary); - } - &:hover::before { - background-color: var(--colour-full); - } - p { - &::after { - @apply hidden; - } - } + @apply blockquote-violet-500; } } diff --git a/src/lib/blog/Post.svelte b/src/lib/blog/Post.svelte index 057f62b..9fcc319 100644 --- a/src/lib/blog/Post.svelte +++ b/src/lib/blog/Post.svelte @@ -3,6 +3,9 @@ </script> <script lang="ts"> + import { building, dev } from '$app/environment'; + import { page } from '$app/state'; + import { parsePostMetadata, type Post } from './Post'; let { @@ -16,6 +19,10 @@ let meta = $derived(parsePostMetadata(post.metadata)); let PostComp = $derived(post.default); + + let ignorePublishedStatus = $derived( + !building && page.url.searchParams.has('ignore-unpublished'), + ); </script> <svelte:head> @@ -23,14 +30,41 @@ <meta name="description" content={meta.blurb} /> </svelte:head> +{#snippet unpublished()} + <div + class="p-4 rounded-lg bg-neutral-950 border-accent-primary border-[1.8px] my-4 sticky top-4 z-50" + > + <h1 class="text-xl mb-2">Unpublished</h1> + <p class="my-1.5 leading-5"> + This article has either not yet been published or has been retracted.<br + /> + This could be due to being unfinished, factual errors, bad formulations, pending + significant corrections, or any number of other reasons. + </p> + {#if !ignorePublishedStatus && dev} + <p class="mt-1 5"> + <a + href={new URL('?ignore-unpublished=+', page.url).href} + class="quicklink">Ignore and read anyway</a + > + </p> + {:else if ignorePublishedStatus} + <p class="mt-1.5">Here be dragons.</p> + {/if} + </div> +{/snippet} + <div class="flex justify-center"> <div class="max-w-2xl w-full"> <h1 class="font-space-grotesk text-5xl mt-8 mb-4"> - <span class="text-accent-primary select-none">: </span - >{meta.title}<span class="text-accent-primary select-none">;</span> + <span class="select-none" + ><span class="text-red-400">❯ </span> less + <span class="text-red-400">'</span></span + >{meta.title}<span class="text-accent-primary select-none">';</span> </h1> <p class="my-4 font-genericmono flex gap-1 md:gap-2 flex-wrap"> - <a href={encodeURI(`../`)} class="quicklink">../</a>{#if filename}<span + <a href={encodeURI(`../`)} class="quicklink">../</a + >{#if filename && (meta.published || ignorePublishedStatus || dev)}<span class="select-none">▒</span ><a href={encodeURI( @@ -45,8 +79,15 @@ .toISOString() .split('T')[0]} </p> - <article id="postmd"> - <PostComp /> - </article> + {#if meta.published || ignorePublishedStatus} + {#if !meta.published} + {@render unpublished()} + {/if} + <article id="postmd"> + <PostComp /> + </article> + {:else} + {@render unpublished()} + {/if} </div> </div> diff --git a/src/lib/blog/Post.ts b/src/lib/blog/Post.ts index 440dffa..aa4c422 100644 --- a/src/lib/blog/Post.ts +++ b/src/lib/blog/Post.ts @@ -9,6 +9,7 @@ export type PostMetadata<Parsed extends boolean = false> = { id: string | number; created: Parsed extends true ? Date : string; updated: Parsed extends true ? Date : string; + published: boolean | 'unlisted'; }; export type Post<MetadataParsed extends boolean = false> = { metadata: PostMetadata<MetadataParsed>; diff --git a/src/lib/console.ts b/src/lib/console.ts index 82562cd..3e44e4b 100644 --- a/src/lib/console.ts +++ b/src/lib/console.ts @@ -115,6 +115,9 @@ margin-right: 4px; [`WARNING, NUKING IS NOW LEGAL... WORLDWIDE!!! >~<`], [`Welcome back, Anon`], [`when you suckin on her nuts`], + [`catboys,,,`], + [`\${MESSAGE_OF_THE_DAY}`], + [`i will mo your td`], [`wait.. nuking's actually kinda hot......`], [`you know what happens after a nuke? smoke %c#3`, "color: #ff0000;"], [`Fucking the industry's transfems since 20xx`], diff --git a/src/lib/storage.ts b/src/lib/storage.ts index 60ecfa3..0a5334d 100644 --- a/src/lib/storage.ts +++ b/src/lib/storage.ts @@ -1,4 +1,4 @@ -import { StorageManager } from '@dmpmem/storage' +import { StorageManager } from '$/lib/vendor/storage' export const localStorage = new StorageManager('local', 'mem.estrogen.zone:'); export const sessionStorage = new StorageManager('session', 'mem.estrogen.zone:'); diff --git a/src/lib/vendor/storage b/src/lib/vendor/storage new file mode 160000 +Subproject 21e62b1642b299bf6b60d956045e1848486a70d diff --git a/src/routes/blog/+page.svelte b/src/routes/blog/+page.svelte index 3ce7b0a..fb4592a 100644 --- a/src/routes/blog/+page.svelte +++ b/src/routes/blog/+page.svelte @@ -3,6 +3,14 @@ import type { PageProps } from './$types'; let { data }: PageProps = $props(); + let posts = $derived( + Object.entries(data.posts) + .filter((v) => v[1].metadata.published === true) + .toSorted( + ([_, a], [__, b]) => + b.metadata.updated.getTime() - a.metadata.updated.getTime(), + ), + ); </script> <svelte:head> @@ -12,43 +20,55 @@ <div class="flex justify-center"> <div class="max-w-2xl w-full"> <div class="font-genericmono"> - <h1 class="font-space-grotesk text-5xl mt-8 mb-4"> - <span class="text-red-400 select-none">❯ </span>ls - <span class="text-red-400">'</span>blog posts<span class="text-red-400" - >'</span - ><span class="text-red-400 select-none">;</span> - </h1> - {#each Object.entries(data.posts) as [_filename, post]} - <div class="flex gap-2"> - <div class="flex flex-col items-end"> - <a - href={resolve('/blog/[id=int]-[slug]', { - id: post.metadata.id.toString(), - slug: post.metadata.slug, - })} - class="quicklink">link</a - > - <a - href={resolve('/blog/[id=int]', { - id: post.metadata.id.toString(), - })} - class="quicklink">short</a - > - </div> - <div class="flex flex-col"> - <table> - <tbody> - {#each Object.entries( { ...post.metadata, slug: undefined, id: undefined, created: undefined, updated: undefined }, ).filter((v) => v[1] !== undefined) as v} - <tr> - <td class="pr-1 align-top">{v[0]}:</td> - <td class="pl-1 align-top">{JSON.stringify(v[1])}</td> - </tr> - {/each} - </tbody> - </table> - </div> + {#if posts.length} + <h1 class="font-space-grotesk text-5xl mt-8 mb-4"> + <span class="text-red-400 select-none">❯ </span>ls + <span class="text-red-400">'</span>blog posts<span + class="text-red-400">'</span + ><span class="text-red-400 select-none">;</span> + </h1> + <div class="flex gap-4 flex-col"> + {#each posts as [_filename, post]} + <div class="flex gap-2"> + <div class="flex flex-col items-end"> + <a + href={resolve('/blog/[id=int]-[slug]', { + id: post.metadata.id.toString(), + slug: post.metadata.slug, + })} + class="quicklink">link</a + > + <a + href={resolve('/blog/[id=int]', { + id: post.metadata.id.toString(), + })} + class="quicklink">short</a + > + </div> + <div class="flex flex-col"> + <table> + <tbody> + {#each Object.entries( { ...post.metadata, slug: undefined, id: undefined, created: undefined, updated: undefined, published: undefined }, ).filter((v) => v[1] !== undefined) as v} + <tr> + <td class="pr-1 align-top">{v[0]}:</td> + <td class="pl-1 align-top">{JSON.stringify(v[1])}</td> + </tr> + {/each} + </tbody> + </table> + </div> + </div> + {/each} </div> - {/each} + {:else} + <h2 class="font-space-grotesk text-5xl mt-8 mb-4"> + <span class="text-accent-primary select-none">zsh: </span>no + matches found<span class="text-accent-primary select-none">.</span> + </h2> + <p class="mt-1.5"> + Feel free to check back once some posts are published. + </p> + {/if} </div> </div> </div> diff --git a/src/routes/blog/[id=int]/+page.svelte b/src/routes/blog/[id=int]/+page.svelte index 84d33ef..1d8d691 100644 --- a/src/routes/blog/[id=int]/+page.svelte +++ b/src/routes/blog/[id=int]/+page.svelte @@ -7,25 +7,25 @@ import { page } from '$app/state'; import { forceTrailingSlash } from '$/lib'; import Post from '$/lib/blog/Post.svelte'; + import { building } from '$app/environment'; let { data }: PageProps = $props(); - let route = $derived( + let route = () => forceTrailingSlash( resolve('/blog/[id=int]-[slug]', { id: data.post.metadata.id.toString(), slug: data.post.metadata.slug, }), - ), - ); + ) + (building ? '' : `${page.url.search}${page.url.hash}`); onMount(() => { - tick().then(() => replaceState(route, page.state)); + tick().then(() => replaceState(route(), page.state)); }); </script> <svelte:head> - <link rel="canonical" href={new URL(route, page.url).href} /> + <link rel="canonical" href={new URL(route(), page.url).href} /> </svelte:head> <Post post={data.post} filename={data.filename} /> diff --git a/src/routes/blog/_posts/+page.server.ts b/src/routes/blog/_posts/+page.server.ts new file mode 100644 index 0000000..bc9d5d9 --- /dev/null +++ b/src/routes/blog/_posts/+page.server.ts @@ -0,0 +1,13 @@ +import { parsePost, type Post } from '$/lib/blog/Post.svelte' + +const posts = import.meta.glob("../posts/*.svx") as Record<string, () => Promise<Post>> +const returnedData = Promise.all(Object.entries(posts).map(v => v[1]().then(r => [v[0], { + ...parsePost(r as Post), + default: null, +}] as const))); + +export const load = async () => { + return { + posts: Object.fromEntries(await returnedData) + } +} diff --git a/src/routes/blog/_posts/+page.svelte b/src/routes/blog/_posts/+page.svelte new file mode 100644 index 0000000..2f7ca25 --- /dev/null +++ b/src/routes/blog/_posts/+page.svelte @@ -0,0 +1,32 @@ +<script lang="ts"> + import { resolve } from '$app/paths'; + import type { PageProps } from '../_/$types'; + import { browser } from '$app/environment'; + import { onMount, tick } from 'svelte'; + import { goto } from '$app/navigation'; + + let { data }: PageProps = $props(); + let posts = $derived(Object.entries(data.posts)); + onMount(() => { + requestAnimationFrame(() => goto(resolve('/blog/'))); + tick().then(() => goto(resolve('/blog/'))); + }); +</script> + +{#if !browser} + {#each posts as a} + <!-- svelte-ignore a11y_consider_explicit_label --> + <a + href={resolve('/blog/[id=int]-[slug]', { + id: a[1].metadata.id.toString(), + slug: a[1].metadata.slug, + })} + ></a> + <!-- svelte-ignore a11y_consider_explicit_label --> + <a + href={resolve('/blog/[id=int]', { + id: a[1].metadata.id.toString(), + })} + ></a> + {/each} +{/if} diff --git a/src/routes/blog/base-post.svx b/src/routes/blog/base-post.svx index 394eaa2..5a0fa13 100644 --- a/src/routes/blog/base-post.svx +++ b/src/routes/blog/base-post.svx @@ -8,6 +8,7 @@ id: -1 # Timestamps are in ISO8601 UTC (`date -u +%Y-%m-%dT%H:%M:%SZ`) created: "2026-01-14T01:25:14Z" updated: "2026-01-14T01:25:14Z" +published: true --- # Base Post diff --git a/src/routes/blog/posts/alpine-ssh-early-initfs.svx b/src/routes/blog/posts/alpine-ssh-early-initfs.svx new file mode 100644 index 0000000..b445127 --- /dev/null +++ b/src/routes/blog/posts/alpine-ssh-early-initfs.svx @@ -0,0 +1,113 @@ +--- +title: "Launching SSH during early boot with mkinitfs" +blurb: "Replacing the early init with our own script to launch SSH, killing it in early userspace, and allowing remote disk decryption in the mean time" +author: "7222e800" +slug: "alpine-ssh-early-initfs-disk-decryption" +id: 1768406136 + +# Timestamps are in ISO8601 UTC (`date -u +%Y-%m-%dT%H:%M:%SZ`) +created: "2026-01-14T15:53:57Z" +updated: "2026-01-14T15:53:57Z" +published: false +--- + +<blockquote class="default-blockquote blockquote-red-400"> + + **Warning**<br/> + This post talks about operations that may lead the reader's system + unbootable, including modifying its initramfs. + Do not follow along if the reader is not prepared to fix its own + systems from potentially infuriating bugs. + + <p><small class="text-sm opacity-50 hover:opacity-70 transition-opacity">Maybe avoid following along on the friday + before the reader's vacation.</small></p> + +</blockquote> + +For a while, this one's been meaning to setup an early-boot SSH environment for +Alpine Linux on systems that are using a +[System Disk](https://wiki.alpinelinux.org/wiki/System_Disk_Mode) installation +mode. + +<!-- TODO: APKOVL boot article --> +In [Data Disk](https://wiki.alpinelinux.org/wiki/Data_Disk_Mode) mode, it can +be handled in `boot` or `sysinit`. This can even be nicely netbooted via a +netbooted apkovl - article on that eventually. (for now, if you're interested +in that, here's a good starting point: +[alpine/mkinitfs#cc4954b/initramfs-init.in](https://gitlab.alpinelinux.org/alpine/mkinitfs/-/blob/cc4954bc73cf55833b48624232b9c42ca3abc390/initramfs-init.in#L647)) + +On System Disk installations, with tooling like [dracut](https://wiki.gentoo.org/wiki/Dracut), +this would also be trivial. Unfortunately, this one's a masochist and like +staying close to the intended upstream Alpine installation + +> **Note**<br/> +> Alpine does have +> [a package](https://pkgs.alpinelinux.org/package/v3.23/community/x86_64/dracut) +> for dracut, and the reader may want to look into using it instead. + +## mkinitfs and it's challenges + +Alpine's [mkinitfs](https://gitlab.alpinelinux.org/alpine/mkinitfs/) allows us +to do things like including files or kernel modules in the image, via their +[features.d](https://gitlab.alpinelinux.org/alpine/mkinitfs/-/tree/master/features.d). + +This is nice and all, but on it's own, we can only really give the kernel a +module, or a file we manually call by spamming enter through the encryption +password prompts and running via the 'Emergency Shell'. + + +### approach 1: patching + +The easiest solution this one thought of was to just directly modify +`/usr/share/mkinitfs/initramfs-init` and be happy, then deal with it when +upstream modifies things. + +This is, however, very not "set, make a blog post, and forget". So we skipped +that idea. + +### approach 2: fork the upstream + +The second, and nicest to upstream solution it thought of was modify +[initramfs-init.in](https://gitlab.alpinelinux.org/alpine/mkinitfs/-/blob/cc4954bc73cf55833b48624232b9c42ca3abc390/initramfs-init.in) + \- which gets built into `/init` in the initfs. + +This would've worked very well, but would require maintaining one's own +`/init`, for which the distro provides few guarantees that the rest of the +tooling will forever accomodate an old version forked off. + +#### upstreaming + +It also thought about upstreaming this, for which this would've been the only +viable approach. However, with this, comes the challenge of cleaning up the +sshd during early openrc, cleanly, without any use-case edge-cases. + +> **For Alpine Maintainers**<br/> +> If beings involved in the mkinitfs project want something like this, this +> one's willing to, with some guidance on avoiding user edge-cases, contribute +> this. + +### approach 3: third approach's the charm + +The third approach was wrapping +[nlplug-findfs](https://gitlab.alpinelinux.org/alpine/mkinitfs/-/blob/cc4954bc73cf55833b48624232b9c42ca3abc390/nlplug-findfs.1.in), +replacing the system-wide binary, regenerating the initramfs, and then spawning +SSH from there and directly interacting via libssh. + +Whilst this would be sensible in theory, this has a high maintenance burden if +nlplug-findfs is modified substantially (or, worse, entirely removed). This is +heavily in no guarantees provided land. + +So this one opted against it. + +### approach 4: a kernel module + +A kernel module could start a userspace process early, which would not involve +touching any of the existing tooling's code (and just needs one file added). + +But also, no. + +## starting ssh before `initramfs-init.in` + +> "it's like LogoFAIL for your initfs" - somebeing, probably + +After roughly a few moments too many than this one'd prefer ot have thought about this, it came to the idea of [TODO: FINISH POST] diff --git a/src/routes/blog/posts/test-post.svx b/src/routes/blog/posts/test-post.svx index b12b25a..f828949 100644 --- a/src/routes/blog/posts/test-post.svx +++ b/src/routes/blog/posts/test-post.svx @@ -1,5 +1,5 @@ --- -title: "Test Post" +title: "Test Post: How Testing a Post can Post your Tests" blurb: "Awawawa Ipsum dolor the neobot is in the washing machine" author: "7222e800" slug: "test-post" @@ -8,6 +8,7 @@ id: -1 # Timestamps are in ISO8601 UTC (`date -u +%Y-%m-%dT%H:%M:%SZ`) created: "2026-01-14T01:25:14Z" updated: "2026-01-14T01:25:14Z" +published: unlisted --- <script lang="ts"> diff --git a/src/routes/canary/+page.svelte b/src/routes/canary/+page.svelte index 2bb9142..c3104ba 100644 --- a/src/routes/canary/+page.svelte +++ b/src/routes/canary/+page.svelte @@ -1,10 +1,10 @@ <script lang="ts"> import { goto } from '$app/navigation'; - import { base } from '$app/paths'; + import { resolve } from '$app/paths'; import { onMount } from 'svelte'; onMount(() => - goto(base + '/canaries/', { + goto(resolve('/canaries/'), { replaceState: true, }), ); diff --git a/src/routes/shared.ts b/src/routes/shared.ts index 2fc9aac..e95ded3 100644 --- a/src/routes/shared.ts +++ b/src/routes/shared.ts @@ -391,6 +391,35 @@ export const ttyLines: TTYText[] = (() => { colour: '#7a7a7a', }, ], + ['blog: ', { colour: '#cdcdcd' }], + ); + wait(100); + replaceLast((v) => [ + ...v, + [ + '/~mem/blog/', + { + colour: '#99aaff', + underlined: true, + weight: 700, + url: 'currenttab:/blog/', + }, + ], + [ + ' (WIP)', + { + colour: '#999999', + }, + ], + ]); + wait(100); + text( + [ + ' - ', + { + colour: '#7a7a7a', + }, + ], ['In random hackerspaces', { colour: '#aaaaaa' }], ); wait(600); |