[RE: nyman]#_

don't let perfect be the enemy of bad

Recent Posts

Setting up your own mailinator domain

published on

Mailinator is one of those great forever services which I seriously hope will never disappear, it has saved me from so much “newsletters” and other things I don’t want in my mailbox.

Sadly it for some reason people think it’s a good idea to block it sometimes.

Which makes absolutely no sense to me. I mean, I am clearly indicating that I do not want email from you. If you force me to give my own email, and send me emails (which we know you will) I will put them into spam.

I am quite certain Google uses that as a signal to start feeding your newsletter into spam for the users who might actually want it.

Either way, that aside.

Your own mailinator domain

This seems to work with most services which tries to prevent mailinator.com and the other known mailinator domains from being used.

You will need your own domain and be somewhat comfortable with editing the DNS of it for this to work. If you are, there is no risk with doing this. If you do not have your own domain, you can get a free subdomain at afraid.org.

You can do this two different ways, either create a domain which is a CNAME for mail.mailinator.com, this is more easily detected if they are motivated.

The other way is to do the following

  1. create a A record pointing to the same destination as mail.mailinator.com (you have to look it up, it ends at .11.30) , for example nospam.your.domain
  2. then create a MX record like nospam.your.domain 10:nospam.your.domain.

Success!

Mailinator does not seem to mind doing this, at least in 2008 https://mailinator.blogspot.com/2008/01/your-own-private-mailinator.html

Using the A method hides it, but sometimes it still does not work. I assume some people have blacklisted the [redacted].11.30 IP which mailinator uses as MX. That IP has been the same for as long as I’ve used them and probably longer.

Enjoy.

Reviewing the charities annual reports

published on

This is a follow up to [this](blog.nyman.re/2025/08/2… and this.

So we’re continuing the quest to figure if it’s realistic for a normal person in the tech industry to do a life-saving donation.

Let’s look closer at the charities listed last time by reading their yearly reports.

Medeor’s report for 2024

Overall a very good report. Very transparent. From this we can read that they have received, around 34 million in funding. They have a lot of different projects ongoing in different countries.

KidsOR Impact report

This is not the same as the meteor one, it’s a flashy html page. It does not have any numbers, but those are available in the annual financial report.

Their yearly donations are GBP 5,6M in 2023 and 10,6M in 2024. They used 7M for charitable activities and 400K for fundraising.

Chain of Hope’s Annual Review

Annual donations in the range GBP 4,4 M 2023 to 3,4M in 2024. Notable there is that the document says 6% of the money went to support costs, and 16% to cost of generating funds. Without diving deeper into this, it looks higher than the others but it might just be that they are more transparent.

I think based on this, I’m leaning towards KidsOR. And as next step I’ll probably try to figure out if my donation would be big enough that it could be “easily” earmarked so I can know what difference it made.

Medeor says that anything above 1000 euro is considered a large donation, big enough for the donor to contact them beforehand to discuss.

I could not find any info about that on KidsOR.

Dishonest game developers

published on

Note This is a very old blog draft, all the way from 2019 when you could still download Fortnite on your iPad (which you can again but it’s a bit harder and you need to live in EU). But the point still stands I need to publish something. Enjoy, or not :-)

I recently tried Fortnite for the first time, thinking I would check out what the fuzz was about. So I install it on the iPad and get started. Knowing roughly how to play it, I can’t even find a weapon in the first round and get hosed by obvious AI players (henchmen). No worries, I try again… and I go on to beat the crap out everyone the two next rounds, Victory Royale, numero uno, #1. Damn I’m good.. or am I?

I do think I’m above the average gamer but this seems a bit suspicious. Maybe they have some matchmaking and my hideous performance in the first match put me in the lowest bracket?

Or… Maybe the other players were not players but really bad bots? A quick googling confirms that indeed, Epic added bots last autumn (editors note, autumn of 2019).

And there does not seem to be any obvious way to identify them, except that people seem to agree that they are quite bad.

So turns out I am not really good at this game?… sad trombone

I spent some time thinking about this, and I read an article discussing it. Turns out not everybody was happy with this silent introduction of bots. Making people think they are better than they are to make them enjoy the game more does seem, wrong on some level.

Or is it? What is the goal from playing games? I guess most people are not looking to become full time pro-gamers but rather just want to have some fun.

Is it wrong to let them enjoy it? Not everyone enjoys loosing. I recall playing StarCraft 2, where the matchmaking was good and made sure you lost roughly half the time by matching you against better players every time you improved. And that was not always fun. Because when you’re perfectly matched it might well be that you play five games in a row and loose all of them. It was one of the reasons I did not play ladder SC2 but just fooled around in coop and other stuff.

But if we accept that developers cheat a bit to allow us to enjoy the game more. What is “a bit”. I am not sure there are any easy answers, it all depends.

How hard do we want our games to be? I guess the most fun part if you believe you win by a close margin.

And then again, Fortnite was not the first game to do this. Mariokart might have been one of the earlier ones to do it. It really skews the game to give worse players a chance. When you’re in the lead or close to it, you will just pick up bad items. While if you’re at the back you get all the good suff. And in single player the computer automatically adapted to your speed.

Asphalt 9 (at least on iPad) is also a obvious example of this, try playing a single player map with a massively over-powered car (compared to what is the recommended level on that course). Now watch how every computer keeps up/stays ahead until ~70% of the course is done. Then falls behind massively on the last 20% to make sure you win if you “should” win (based on the points in your car).

To conclude, I’ll just say I don’t know what is the right way. If the developers made it too obvious that they are letting you win, most people would not think that to be fun. But on the other hand, people like winning more than loosing.

box-art.css

published on

Today we’re onto something lighter again. A box-art/nfo-style css, in as part of experimenting with a new look for this blog.

This is harder than it looks to do in CSS, but I enjoyed the challenge.

This is also something the LLM’s failed quite hard, but at least claude wrote me a simple javascript page where I could tweak the parameters until it worked.

Screenshot from some text, wrapped by BOX ASCII. Text is just placeholder. Dark background, green text.

Also.. I just found this, it’s just beautiful int10h.org also this is also great panr.github.io/terminal-…

Finding a charity where you can see the impact

published on

This is a follow up to the previous one where we defined the question. Read that first

Can I make a direct life saving impact to someone, where my action, with a high degree of probability, leads to the survival of someone who might otherwise not have survived.

If the action is a donation, and the answer is Yes, and I have the means. Then I want to do it.

But let’s clarify what this means in practice.

  1. It must be a monetary donation. Although volunteering is important, I am not in a place where I can do that now.
  2. The money needs to go more or less directly to the cause, preferably quite soon.
  3. There has to be some feedback from the cause.
  4. EU based which accepts IBAN or other ways of receiving donations without paying the credit card companies x%.

3. Feedback loop

This is the main new part for my charitable donations. Previously and currently, my charitable donations go to organisations who I believe do good. But there is little or no feedback loop, so I don’t know what good my money does.

I am doing this exercise, to figure out if there is a more direct way.

When I started, my original idea was to find something where I donate directly to a cause. Like a heart operation for Person X, which would otherwise not happen.

Based on my research, I could not find that. The closest I found was watsi which has names and pictures of the people you help. My impression is that it’s a low-income version of gofundme.

After reviewing the people in need there, it does not meet my criteria for life saving. And when thinking about it, thats a great thing. After all, it would be horrible if there was a website with a list of people who would die unless they raised enough money.

So I expanded my search, instead of trying to find a place with named persons, which adds a lot of overhead I started looking if there would be a way to donate to a clinic, or some other semi direct intervention.

List of potential charities

(Note, I have done only brief research on these and there is likely factual errors. It’s just my personal opinion and not facts)

Chain of Hope (and La Chaîne de l’Espoir)

website website

Focusing on cardiac surgery, especially for kids. Definitely life saving but they seem quite big and from a brief check I don’t see any feedback loop.

KidsOR

website Builds/improves Operating Rooms abroad. Ships out equipment from UK and installs it, trains the technicians and then helps maintain it. Important and valuable but seems there will be quite a long time between my donation and when it would help someone.

Medeor

website

German organisation focusing on providing medicine and equipment, not personnel or health interventions. Seems promising on the feedback part as it says that donations of 1000 euro and above are counted as “major donations” which receives additional feedback. As they are German, IBAN is possible.

Next steps

I still have a bunch of charities to review. Finding the “perfect” charity is not easy, and it would be easier just to pick one and give. But my hopes is that I will find one I really like, which will have a tight enough feedback loop that I feel I made a difference. This in turn, will cause me to want to give more. After all, if I can help, why wouldn’t I?

Currently, the answer is, because even if I donated everything I owned to the Red Cross, it would help someone, but it wouldn’t be effective use of my money.

Large charities are important, they can do work that nobody else can, but small ones are also important. The big ones can’t be everywhere, and there are people in need in a lot of places.

Same thing about long term vs short term donations. Both are important.

Next step here will hopefully be a decision, and motivation and explanation of why. And then later, hopefully a follow up on what kind of difference it made.

What can YOU do?

published on

The world is a big place. Really big. There are a lot of people in it.

And because there are so many people, it means there are a lot of people who, at this moment needs help.

So what can you do? A lot. That’s a too wide question.

Let’s narrow it down to a very specific question.

Can you save the life of someone in need?

Again, yes, there are lots of ways you could, but also a lot of ways which won’t work. The question is still too wide.

Let’s narrow it down more. Let’s focus on money.

How much money would you need to spend to save the life of someone?

If you ask the effective altruism people, it’s very little, just pay for some malaria nets and vaccines and the chances are that you’ll save someone.

But that is very indirect. It might be a cost effective way to save a life, but it will be very hard to know if your money saved a life or not. Statistically, if you buy enough nets it will.

But if you wanted to feel like you made an impact right now, or within the near future so that your giving feels more direct. And you wanted to know with a high degree of certainty that it had a impact, say within the coming three months.

Now we’re close to narrowing down our question which I’ll dive in to in a coming blog post.

Can I make a direct life saving impact to someone, where my action, with a high degree of probability, leads to the survival of someone who might otherwise not have survived.

What does that cost? And what ways are there to do this.

published on

vibecoding feels very productive, but often I’ve noticed it’s just a feeling, I actually know better than the LLM what the problem is, and could fix it faster, but for some reason I get stuck prompting it again and again

not sure why

Hopefully the psychology departments are looking at this

My favourite podcasts

published on

Security Podcasts I Enjoy

In special order, roughly in the order of which I remembered them which says something about how much I listen to them.

Risky Business - the main feed, compact and respects my time. I haven’t missed in on a long time. They have expanded and have other great podcasts now, including a news bulletin. I listen to those sometimes.

Three Buddy Problem - Quite new podcast, entertaining listen but the opposite of Risky Biz when it comes to being compact. Fun mix of Juan ranting and Costin providing deep technical commentary.

Security Cryptography Whatever - Great podcast, very technical, melts my brain sometimes but I feel I learn something every episode.

Säkerhetspodcasten - Swedish podcast, very entertaining, mix of somewhat serious episodes and “unstructured” episodes.

Defence In Depth - Each episode is based on a linked in discussion about some topic. Short and focused episodes. A lot of discussions from the point of CISO or security leaders. I’d recommend looking through their archive and picking a topic you’re interested in.

Darknet Diaries - The best story-telling podcast on infosec, hacking culture and internet-crime related topics. Targets a wider audience so he will explain terms and technologies in detail. Also he has strong idealogical views which he likes to share.

The above are continuously running podcasts, I’ll also mention a few which are just a set of episodes focusing on one topic, and ones that has ended. Also these are not really security focused, but rather technical ones.

Scam Inc - A podcast about the pig butchering scam centres, the scams they do and the victims. The only one on the list that is paid. You can listen to the two first ones for free to see if it’s your thing.

The Lazarus Heist - About the North Korean hacking group called Lazarus Groups and their various hacks. BBC level production quality.

13 minutes(to the moon) - Started with Apollo 11 and has since expanded into related topics.

The Bomb - Also BBC podcast, about the development of the atomic bomb.

Omega Tau Podcast - Super interesting deep-dives into various topics by interviewing people in the area. They did both German and English episodes.

Twenty Thousand Hertz - A podcast about sound. The quality is what you’d expect from someone who seems obsessed with sound and audio. Has some interesting episodes. I’m definitely no audiophile but I like when experts explain things. Check out the episode on the THX Deep Note or maybe the Zelda ones.

Hopefully you find something interesting here. If you’re familiar with these podcasts and have some other that you think I’d enjoy, reach out to me on mastodon or send a email.

A simple devcontainer for your agent with eyes (browser and screenshot capabilities)

published on

These instructions have been tested on a M1 MacBook with podman, your mileage may vary. Note that running playwright/chrome as root might be dangerous so don’t use this for scraping or untrusted content unless you know what you’re doing.

Actually, never feed untrusted content into your LLM and always sandbox it as much as possible. Otherwise you will sooner or later be a sad panda.

Instruction

Put this into .devcontainer/devcontainer.json

{
  "name": "Playwright Dev Environment",
  "image": "mcr.microsoft.com/playwright:v1.55.0-noble",
  "postCreateCommand": "npm install -g @anthropic-ai/claude-code",
  "customizations": {
    "vscode": {
      "extensions": [
        "Anthropic.claude-code"
      ]
    }
  },
  //"remoteUser": "pwuser",
  "runArgs": [
    "--ipc=host",
    "--security-opt=seccomp=unconfined"
  ],
  "capAdd": [
    "SYS_ADMIN"
  ],
  "mounts": [
    "source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume",
    "source=claude-code-config-${devcontainerId},target=/home/pwuser/.claude,type=volume"
  ],
  "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
  "workspaceFolder": "/workspace",
  "containerEnv": {
    "NODE_OPTIONS": "--max-old-space-size=4096",
    "CLAUDE_CONFIG_DIR": "/home/pwuser/.claude"
  }
}

Then create something like this (or ask your llm to do it)

#!/usr/bin/env node
// Playwright-based headless renderer with console capture
// Usage:
//   node pw-screenshot.mjs --url https://example.com --out out.png
//   node pw-screenshot.mjs --html-file page.html --out out.png
//   echo "<h1>Hello</h1>" | node pw-screenshot.mjs --html-stdin --out out.png

import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import { chromium } from 'playwright';

function parseArgs(argv) {
  const args = {
    url: null,
    htmlFile: null,
    htmlStdin: false,
    out: 'screenshot.png',
    wait: 'networkidle', // playwright: load | domcontentloaded | networkidle
    fullPage: true,
    timeout: 60000,
    viewport: '1280x800',
    console: null,
    emulateMedia: null,
  };
  for (let i = 2; i < argv.length; i++) {
    const k = argv[i];
    const v = argv[i + 1];
    switch (k) {
      case '--url': args.url = v; i++; break;
      case '--html-file': args.htmlFile = v; i++; break;
      case '--html-stdin': args.htmlStdin = true; break;
      case '--out': args.out = v; i++; break;
      case '--wait': args.wait = normalizeWait(v); i++; break;
      case '--fullpage': args.fullPage = v !== 'false'; if (v === 'false') i++; break;
      case '--no-fullpage': args.fullPage = false; break;
      case '--timeout': args.timeout = Number(v); i++; break;
      case '--viewport': args.viewport = v; i++; break;
      case '--console': args.console = v; i++; break;
      case '--emulate-media': args.emulateMedia = v; i++; break;
      case '--help':
      case '-h': printHelp(); process.exit(0);
      default: break;
    }
  }
  return args;
}

function normalizeWait(v) {
  const val = String(v).toLowerCase();
  if (val === 'networkidle0' || val === 'networkidle2') return 'networkidle';
  if (val === 'domcontentloaded' || val === 'load' || val === 'networkidle') return val;
  throw new Error(`Invalid --wait '${v}'. Use load|domcontentloaded|networkidle`);
}

function printHelp() {
  const help = `
Usage:
  node pw-screenshot.mjs [--url URL | --html-file FILE | --html-stdin] [--out PATH]

Options:
  --url URL            Navigate to the URL and render.
  --html-file FILE     Load HTML from file and render.
  --html-stdin         Read HTML from stdin and render.
  --out PATH           Screenshot output path (default: screenshot.png).
  --wait MODE          load | domcontentloaded | networkidle (default: networkidle).
  --fullpage BOOL      true/false; or --no-fullpage (default: true).
  --timeout MS         Navigation/content timeout in ms (default: 60000).
  --viewport WxH       Viewport, e.g., 1280x800 (default: 1280x800).
  --console PATH       Write page console JSONL to PATH or '-' for stdout (default: stderr only).
  --emulate-media TYPE Emulate media type: screen | print.
  --help               Show this help.
`;
  process.stderr.write(help);
}

function parseViewport(spec) {
  const m = String(spec).toLowerCase().trim().match(/^(\d+)x(\d+)$/);
  if (!m) throw new Error(`Invalid --viewport '${spec}'. Expected WxH, e.g., 1280x800`);
  return { width: Number(m[1]), height: Number(m[2]) };
}

function openConsoleStream(dest) {
  if (!dest) return null;
  if (dest === '-') return process.stdout;
  return fs.createWriteStream(dest, { flags: 'a' });
}

function writeConsole(stream, record) {
  const line = JSON.stringify(record) + '\n';
  if (stream) stream.write(line); else process.stderr.write(`[console] ${record.type} ${record.text}\n`);
}

async function readStdin() {
  return await new Promise(resolve => {
    let data = '';
    process.stdin.setEncoding('utf8');
    process.stdin.on('data', chunk => { data += chunk; });
    process.stdin.on('end', () => resolve(data));
  });
}

async function main() {
  const args = parseArgs(process.argv);
  if (!(args.url || args.htmlFile || args.htmlStdin)) { printHelp(); process.exit(2); }

  const viewport = parseViewport(args.viewport);
  const consoleStream = openConsoleStream(args.console);

  let browser;
  try {
    browser = await chromium.launch({ headless: true, args: ['--no-sandbox', '--disable-dev-shm-usage'] });
    const context = await browser.newContext({ viewport });
    const page = await context.newPage();

    page.on('console', msg => {
      writeConsole(consoleStream, { ts: new Date().toISOString(), type: msg.type(), text: msg.text() });
    });
    page.on('pageerror', err => {
      writeConsole(consoleStream, { ts: new Date().toISOString(), type: 'pageerror', text: String(err) });
    });

    if (args.emulateMedia) await page.emulateMedia({ media: args.emulateMedia });

    if (args.url) {
      await page.goto(args.url, { waitUntil: args.wait, timeout: args.timeout });
    } else {
      const html = args.htmlFile ? fs.readFileSync(path.resolve(args.htmlFile), 'utf8') : await readStdin();
      await page.setContent(html, { waitUntil: args.wait, timeout: args.timeout });
    }

    await page.screenshot({ path: args.out, fullPage: !!args.fullPage });
    process.stdout.write(`Saved screenshot to ${args.out}\n`);
  } finally {
    if (browser) await browser.close().catch(() => {});
    if (consoleStream && consoleStream !== process.stdout) consoleStream.end();
  }
}

main().catch(err => { process.stderr.write(String(err.stack || err) + '\n'); process.exit(1); });

After that you can instruct claude or some other coding agent (that can view images, e.g. not codex currently) to view the result and describe it.

Hopefully this makes the agents more useful as they can inspect and see if the change they did actually had the expected result.

I have not tested this very much yet, but it’s one of the pet issues I’ve when having agents do any changes to a webpage. The lack of feedback loop for them means I’ve had to view and explain the result.

You cannot hide on the internet

published on

At least not on the IPv4 network, but I would not trust the IPv6 network either, and you have not been able to for a long time.

If you open a port to the whole world, it will get probed. If it’s a popular port like 443 or a sensitive one like 9200, it will get scanned really-fast.

Same goes if you announce it by creating a TLS certificate with a ACME service like Let’s Encrypt. When you do, Let’s Encrypt will publish your certificate on the Certificate Transparency log. And there are multiple entities who sit and watch it and will probe everything on it.

I learned just how fast recently when I was playing around with tailscale funnel, within minutes I had someone from a Russian IP range sending requests my way.

So the tl.dr. is don’t open any services to the wider internet unless you are sure they are safe. If they are more complicated or less battle tested than ssh and nginx, you are better of not exposing it.

Instead use netbird, fireguard, tailscale, zerotier to access things. Or if you can’t or does not want to, hide it behind wildcard TLS certs, or some other way.

Categories

Adblocking (1)

Altruism (3)

Blaugust (27)

Css (2)

Distractions (1)

Draft (16)

Gpt (9)

Gptaoc2022 (9)

Infosec (1)

Linux (2)

Llm (10)

Microblog (2)

Php (1)

Rants (1)

Repair (1)

Security (6)

Servers (4)

Spreadsheets (1)

Sysadmin (4)

Tech (26)

Web (8)