aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.vscode/settings.json3
-rw-r--r--src/app.css8
-rw-r--r--src/lib/blog/Post.svelte53
-rw-r--r--src/lib/blog/Post.ts1
-rw-r--r--src/lib/console.ts3
-rw-r--r--src/routes/blog/+page.svelte90
-rw-r--r--src/routes/blog/[id=int]/+page.svelte9
-rw-r--r--src/routes/blog/base-post.svx1
-rw-r--r--src/routes/blog/posts/alpine-ssh-early-initfs.svx109
-rw-r--r--src/routes/blog/posts/test-post.svx3
-rw-r--r--src/routes/shared.ts29
11 files changed, 256 insertions, 53 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 7b1cffd..29903ed 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -3,6 +3,7 @@
"*.css": "tailwindcss",
},
"files.exclude": {
- "$": true
+ "$": true,
+ "src/lib/vendor/storage": true
},
}
diff --git a/src/app.css b/src/app.css
index c98a44c..4b1b53d 100644
--- a/src/app.css
+++ b/src/app.css
@@ -83,7 +83,7 @@
#postmd {
@apply font-sans;
a {
- @apply quicklink;
+ @apply quicklink text-nowrap;
}
h1 {
@apply text-4xl mb-1.5 mt-2;
@@ -145,7 +145,7 @@
@apply h-[90%] top-[5%];
}
p {
- @apply my-1;
+ @apply my-1.5 leading-5;
}
code {
@apply font-genericmono;
@@ -197,9 +197,9 @@
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%];
+ @apply absolute w-1 bg-violet-500/50 transition-colors left-0 top-[10%] h-[80%];
content: '';
- --colour-full: var(--color-accent-primary);
+ --colour-full: var(--color-violet-500);
}
&:hover::before {
background-color: var(--colour-full);
diff --git a/src/lib/blog/Post.svelte b/src/lib/blog/Post.svelte
index 057f62b..ed1b929 100644
--- a/src/lib/blog/Post.svelte
+++ b/src/lib/blog/Post.svelte
@@ -3,6 +3,9 @@
</script>
<script lang="ts">
+ import { 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(
+ 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">:&nbsp;</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/routes/blog/+page.svelte b/src/routes/blog/+page.svelte
index 3ce7b0a..edd1c46 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,53 @@
<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">❯&thinsp;</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">❯&thinsp;</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:&ThinSpace;</span>no
+ matches found<span class="text-accent-primary select-none">.</span>
+ </h2>
+ <p class="mt-1.5">Feel free to check back later.</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..574ea76 100644
--- a/src/routes/blog/[id=int]/+page.svelte
+++ b/src/routes/blog/[id=int]/+page.svelte
@@ -10,22 +10,21 @@
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,
}),
- ),
- );
+ ) + `${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/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..f284caf
--- /dev/null
+++ b/src/routes/blog/posts/alpine-ssh-early-initfs.svx
@@ -0,0 +1,109 @@
+---
+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
+---
+
+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 if needed, `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'.
+
+## some code
+
+```ts
+/*
+ Copyright (C) 2024-2026 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 Affero, Inc., at version 1.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the Affero General Public License along with this program. If not, see <https://spdx.org/licenses/AGPL-1.0-only>.
+*/
+import { Animation, MultiObjectKeyframe } from '@memdmp/keyframegen';
+import {
+ biosStepInterval,
+ biosSteps,
+ getDelay,
+ login,
+ ttyLines,
+} from './shared.ts';
+import fs from 'node:fs';
+import esbuild from 'esbuild';
+console.log('hi');
+const anim = new Animation();
+let ttyCtr = 0;
+const stages = [
+ anim.selector('.anmroot #bios'),
+ anim.selector('.anmroot #grub'),
+ anim.selector('.anmroot #grub-term'),
+ anim.selector('.anmroot #openrc'),
+ ...ttyLines.flatMap((v) =>
+ v.kind === 'clear' ? [anim.selector('.anmroot #tty-' + ttyCtr++)] : [],
+ ),
+];
+const handleSteps: Step[] = [
+ // (n) => {
+ // toStage(0);
+ // anim.in(500, n);
+ // },
+ ...biosStepHandlers,
+ ...grubStepHandlers,
+ ...openrcStepHandlers(1),
+ ...ttyStepHandlers,
+ (n) => {
+ const s = anim.selector('.anmroot #app .hidden-after-anim');
+ s.style(visibleStyles);
+ anim._internal_timeline.now += 1;
+ s.style(hiddenStyles);
+ anim.in(1000, n);
+ },
+];
+fs.writeFileSync(
+ 'src/routes/anim.css',
+ `${comment}
+${esbuild.buildSync({
+ stdin: {
+ contents: `${exported}
+${tail}`,
+ loader: 'css',
+ },
+ write: false,
+ minify: false,
+ }).outputFiles![0].text
+ }`,
+);
+```
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/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);