MadMode

Dan Connolly's tinkering lab notebook
wordle of my bookmarks circa 2009
dckc quick notes (Jan 25, 2026)
  • Zope: I want my data back · dckc/madmode-blog #34 (Jan 25, 2026)

    My git annex is a mess of tiny files and commits; I'm inclined to start over, but I can't just declare bankruptcy; there's some stuff that I think is nowhere else:

    ~/annex/personal-sites/zope-migrate
    10:55 connolly@bldbox$ git annex whereis ./,zout.log
    whereis ,zout.log (2 copies) 
      	a1f75b71-2e6a-4ca2-b467-6c867c37329d -- connolly@bldbox:~/annex [here]
      	e393a836-90e2-41e9-821d-15d2a149398d -- dckc@ps23:~/annex [ps23.lan_annex]
    
    $ git log10 ./,zout.log
    2025-01-29 20:19 f4c9817d7 git-annex adjusted branch
    2025-01-25 04:16 232471922 git-annex in connolly@bldbox:~/annex
    2025-01-25 04:15 72802ee28 git-annex in connolly@bldbox:~/annex
    2025-01-25 03:32 880859aa6 git-annex in dckc@ps23:~/annex

    See also:

  • GnuCash-backed ERTP: scalable rights transfer grounded in double-entry bookkeeping · dckc/finquick #79 (Jan 19, 2026)

    progress:

    image

    2026-01-18 17:14 a245f27 feat(ertp-ledgerguise): add escrow layer and mint charting

  • blogging analysis paralysis: hakyll, hugo · dckc/madmode-blog #14 (Jan 3, 2026)

    resolved Dec 2024

  • migrate dependencies from Pipfile to uv / pyproject · dckc/madmode-blog #212 (Jan 3, 2026)
  • advent of ocaps for AI · dckc/awesome-ocap #64 (Dec 3, 2025)

    feeling like this falls under "I need another project like I need a hole in the head."

  • advent of ocaps for AI · dckc/awesome-ocap #64 (Dec 3, 2025)

    rust : Hardened JS :: Formal Verification : Capability Security

    Capability security and formal verification are the best tools I see for managing the complexity in modern digital infrastructure. Rust is more of a formal verification tool: the rust compiler absolutely guarantees certain properties of programs. Until runtime, that is -- no matter how correct your code is, it's vulnerable to code that you link with. Capability platforms such as Hardened JS take a different approach: even if some components are faulty or malicious, your code can defend itself against them.

    Even better is when they are combined, as in the rust cap-std library. CHERI processors provide capability security in hardware. Apple's Memory Integrity Enforcement (MIE) and Android Arm Memory Tagging Extension are getting very close!

  • boot guix vm from with hardware disk · dckc/awesome-ocap #65 (Dec 2, 2025)

    error: you need to load the kernel first

    failure mode: when I boot from the hard disk, a UEFI shell starts but doesn't pass control to the next step. I can do so manually; then grub comes up...

    I got a grub boot menu, but when i choose the 1st item, it just comes back. when I choose it again, I get "error: you need to load the kernel first"

    full aider/gemini chat session: ocap-osdev-aider-chat-history.md

  • advent of ocaps for AI · dckc/awesome-ocap #64 (Dec 2, 2025)

    OCaps for AI: Dependencies

    The AI crank in this meme does a great job of expressing my concerns about the destabilizing impact of AI. But just like the Rust rocket is poised to counter-balance that impact, capability-based security, which provides scalable support for the principle of least authority (POLA), could be just as important, if not more.

    Image

    ack: outerheaven

    p.s. Is this the ultimate XKCD “Dependency” derivative? « The Wiert Corner – irregular stream of stuff does a nice job of explaining the cartoon.


    introduction diagram taken from Bringing Object-orientation to Security Programming

  • Netboot/PXE LAN Service for osdev · dckc/madmode-blog #238 (Nov 29, 2025)

    Isolated DHCP Server / segment on a Raspberry Pi class device?

    🎯 Overview and Technical Implementation

    The Isolated DHCP Server strategy is the chosen path to overcome the limitations of consumer mesh Wi-Fi systems (like Google Wi-Fi) which do not allow the configuration of custom DHCP options 66 (next-server) and 67 (filename). This guide serves as the definitive technical implementation plan for this strategy, consolidating all configuration and workflow details.

    This approach involves setting up a dedicated server (like a Raspberry Pi or Mini-PC) to run the DHCP and TFTP/HTTP services on a separate, isolated network segment.

    The "Isolation" Principle

    By running the PXE service on its own subnet, we ensure:

    1. No Conflict: The main home router continues to manage the primary network, preventing the conflict that occurred in Strategy A (Full DHCP on Workstation).
    2. Full Control: The isolated DHCP server has full control over the necessary PXE options (66 and 67), ensuring the target hardware (e.g., ThinkPad T430) receives the correct boot instructions.

    🛠️ Configuration Details (ISC DHCP Server)

    The configuration below uses isc-dhcp-server to manage the isolated subnet 192.168.86.0/24. This setup is crucial as it directs the PXE client to the correct server IP and the appropriate bootloader file based on its architecture.

    Critical Parameters
    Parameter Configuration Value Purpose
    subnet / range 192.168.86.0 / 192.168.86.10 - .200 Defines the address range for the isolated network.
    next-server (Option 66) 192.168.86.62 The IP of the PXE/TFTP Host. All clients are told to look here for the bootloader files.
    filename (Option 67) Conditional based on client architecture (see below). Specifies the initial file the client should download from the next-server.
    dhcpd.conf Configuration Snippet

    The following excerpt from dhcpd.conf implements the conditional boot loading, which is a best practice for modern PXE environments supporting both legacy and UEFI systems.

    # tftpd stuff from netboot.xyz
    option arch code 93 = unsigned integer 16;

    subnet 192.168.86.0 netmask 255.255.255.0 {
    range 192.168.86.10 192.168.86.200; # IP range for PXE clients
    next-server 192.168.86.62; # The static IP of the PXE/TFTP host
    option subnet-mask 255.255.255.0;
    option routers 192.168.86.1; # The gateway for the isolated subnet
    option broadcast-address 192.168.86.255;
    option domain-name-servers 1.1.1.1;

    # Conditional booting logic based on DHCP Option 93 (client architecture)
    if exists user-class and ( option user-class = "iPXE" ) {
    # Clients that report as iPXE get the netboot.xyz menu via HTTP
    filename "[http://boot.netboot.xyz/menu.ipxe\](http://boot.netboot.xyz/menu.ipxe)";
    } elsif option arch = encode-int ( 16, 16 ) {
    # UEFI clients (arch 16, 64-bit) get the UEFI bootloader via HTTP (Best Practice)
    filename "[http://boot.netboot.xyz/ipxe/netboot.xyz.efi\](http://boot.netboot.xyz/ipxe/netboot.xyz.efi)";
    option vendor-class-identifier "HTTPClient";
    } elsif option arch = 00:07 {
    # Other UEFI/x64 clients get the standard UEFI bootloader
    filename "netboot.xyz.efi";
    } else {
    # Legacy BIOS clients get the legacy bootloader
    filename "netboot.xyz.kpxe";
    }
    }

    🚀 OSDev and Docker Integration

    Once the DHCP server is running on the isolated segment, the netboot.xyz Docker container will serve the actual boot files.

    The next critical step is ensuring the local custom OSDev images are accessible:

    • Action: Finalize the docker-compose.yml volumes to map your local OSDev build directory (e.g., ./osdev-builds) into the Docker container's TFTP/HTTP root path.
    • Result: When the ThinkPad T430 requests a file specified by the iPXE menu (served by netboot.xyz), the file will be pulled directly from the local volume, enabling rapid, iterative testing of custom kernels and bootloaders.
  • Netboot/PXE LAN Service for osdev · dckc/madmode-blog #238 (Nov 29, 2025)

    Replace Google Wi-Fi devices with OpenWRT device?

    This document details the requirements and trade-offs for Strategy D: Replace Main Router, which involves upgrading the entire network backbone to a Prosumer-grade device (like the GL.iNet GL-MT6000, or Flint 2) to natively support PXE/DHCP Option 66/67.

    Key Comparison: Google Wi-Fi (Current) vs. GL.iNet GL-MT6000 (Proposed)

    Replacing the router solves the underlying DHCP issue (Strategy B failure) while significantly upgrading network performance (as you noted, the Flint 2 is much faster).

    Feature / Metric Current Router (Google Wi-Fi / Nest Wi-Fi) Proposed Router (GL.iNet GL-MT6000 / Flint 2)
    DHCP Control (PXE) Minimal/None. Critical PXE options (66/67 for TFTP Server IP and Boot File) are unavailable in the consumer-grade firmware. Full Control (Native). Runs OpenWrt/Custom Firmware. Allows granular DHCP settings including Options 66/67, essential for native PXE booting.
    Wired Interface Speed 1 Gigabit (1G). Typical consumer hardware is limited to 1G WAN/LAN ports. 2.5 Gigabit (2.5G). Features 1x 2.5G WAN and 4x 2.5G LAN ports.
    Wired Performance Max theoretical throughput of 1 Gbps. (Slower for local file transfers/OSDev image pushing). Max theoretical throughput of 2.5 Gbps. (250% increase in wired speed, beneficial for large file transfers).
    Processor / RAM Typically lower-spec Quad-Core (e.g., 1.4 GHz) / 1GB RAM. High-performance Quad-Core ARM CPU (2.0 GHz) / 1GB DDR4 RAM.
    Operating System Proprietary / Closed Source. OpenWrt (Open Source). Allows installation of packages like TFTP servers, DNS/DHCP control, and VPN clients directly on the router.
    Complexity / Effort Low administrative effort, plug-and-play. High initial setup effort and configuration complexity. Requires learning OpenWrt UI/concepts.
    Primary Goal Ease of setup, Mesh Networking. Performance, Security, and Advanced Control.

    💡 Rationale for Strategy D

    The primary driver for considering the GL-MT6000 is its native support for OpenWrt, which grants full administrative control over the DHCP server.

    1. Native PXE: The router can be configured directly to serve the necessary PXE boot instructions (TFTP server IP and boot filename), eliminating the need for complex firewall rules or an isolated DHCP server on a dedicated mini-PC (Strategy C).
    2. Performance Boost: The 2.5G Ethernet ports significantly improve wired data transfer speeds, which is a massive quality-of-life upgrade for moving large OSDev build artifacts or accessing network storage.
    3. Future-Proofing: Provides a robust platform for future OSDev/networking needs, such as setting up VLANs for isolation or running lightweight services directly on the router.

    ⚠️ Drawbacks and Effort

    The main obstacle is the High Effort involved: replacing the main router requires configuring and migrating all existing network settings (Port Forwarding, Static IPs, Wi-Fi SSIDs, etc.) to the new device, resulting in full network disruption during the transition.

  • Netboot/PXE LAN Service for osdev · dckc/madmode-blog #238 (Nov 29, 2025)

    Replace Router? Isolated Server?

    1. Networking Infrastructure (The PXE Prerequisite)

    The primary obstacle is ensuring the client machine (T430) receives a DHCP offer correctly pointing it to the boot server.

    Strategy Status Rationale for Status
    Strategy A (Original Attempt): Full DHCP on Workstation Failed (Conflict - 8c65f7e) Running a full DHCP server (isc-dhcp-server) on the primary subnet immediately conflicted with the Google Wi-Fi router's DHCP service.
    Strategy B: Native Router Configuration Infeasible (Dead End) Google Wi-Fi does not expose the custom DHCP options (Option 66/67) required for PXE booting.
    Strategy C: Isolated DHCP Server CONFIGURED (Current Path) Deploy a dedicated server (Mini-PC/Pi) to run DHCP/TFTP on an isolated segment, providing the required PXE options without disrupting the main network. Detailed implementation is captured in the Isolated DHCP Guide.
    Strategy D: Replace Main Router Viable (High Effort) Replaces the entire network backbone with a prosumer solution that provides native, full DHCP control (Option 66/67). This is considered the "cleanest" solution but requires the highest initial effort, cost, and full network disruption.
    misc notes
    2. Boot Service Content (The Content)
    Subgoal Requirement Component Status OSDev Relevance
    Generic Menu Serve a variety of popular OS installers and utilities. netboot.xyz Docker Container Deployed The Docker Compose configuration is complete and the service is ready to run.
    OSDev Custom Image Serve local, custom-compiled kernels/bootloaders for testing. TFTP/HTTP root directory mapping. In Progress This is the crucial step for iterative OSDev testing; the current infrastructure needs final volume mapping.

    3. Critical Remaining Steps

    1. OSDev Integration: Finalize the docker-compose.yml volumes to map a local directory (e.g., ./osdev-builds) to the server's TFTP/HTTP root path. This will make custom-compiled kernels and bootloaders accessible for the T430.
    2. Physical Testing: Conduct the first end-to-end test by booting the ThinkPad T430 on the isolated network segment and verifying it receives the PXE offer, loads the initial bootloader, and successfully displays the netboot.xyz menu.
  • genode sculpt on used laptop with linux vm · dckc/madmode-blog #49 (Nov 29, 2025)

    Did I get a vm working 3 years ago?

    I'm struggling with how to sneakernet? again today; in preparation for taking that question upstream, I'm committing the recent progress:

    2025-11-29 12:06 300bf48 chore(t430): genode persistent config with wm

    In doing so, I find notes on getting a vm actually working:

    2023-01-29 01:05 d1c6d3a feat(t430): boot linux from vbox6 in genode

    Yes! The VM boots and I see the debian installer screen! Thanks!
    -- dckc to genode-users Sat Jan 28 08:54:02 CET 2023

  • genode sculpt on used laptop with linux vm · dckc/madmode-blog #49 (Nov 28, 2025)

    persistent config puzzle solved using Ventoy boot tool

    For a while, I had a very slow debug cycle:

    1. boot nios-minimal-25.05.xyz.iso from USB
    2. Connect to wifi (details below)
    3. download a genode image to RAM disk using w3m
    4. write it to the ssd: sudo dd if=sculpt-25-10.img of=/dev/sda bs=1M conv=fsync
    5. reboot into genode
    6. (fail to) set up a persistent configuration
    Connecting to wifi

    per Networking in the installer docs:

    $ sudo -i
    # systemctl start wpa_supplicant
    # wpa_cli
    > add_network
    0
    > set_network 0 ssid "d..."
    OK
    > set_network 0 psk "..."
    OK
    > enable_network 0
    ...
    <3>CTRL-EVENT-CONNECTED - Connection to ... completed ...

    Then I discovered Ventoy, which lets me put a whole bunch of boot images in one partition of the USB stick and choose among them at boot time.

    Ironically, using one of the images as a boot disk prohibits mounting the partition to see the others! (can't open blockdev)

    Fortunately, Ventoy has an option to leave some unused space when setting up a device. So I put ventoy-1.1.07-linux.tar.gz on /dev/sdb3, booted a small linux distro, and tried to use that to set up the ssd. No joy: mkexfatfs not found. So I booted a beefier distro (gparted? ubuntu mate?) and used that to run bash Ventoy2Disk.sh -i /dev/sda -g -r 100000.

    Then I grabbed a genode image (again) and put it in the Ventoy volume. I booted that, and used inspect to achieve a persistent configuration:

    inspect:> mkdir -p /ahci-0.3/config/25.10; cd config/25.10
    inspect:/ahci-0.3/config/25.10> cp /config/managed/deploy .

    My Ventoy thumb drive now subsumes a whole collection of thumb drives that I used to keep:

    $ ls -1sh
    total 12G
    276M alpine-standard-3.22.2-aarch64.iso
    784M debian-13.2.0-amd64-netinst.iso
    1.9G debian-live-13.2.0-amd64-standard.iso
    256K debian-live-13.2.0-amd64-standard.iso.torrent
    580M gparted-live-1.7.0-8-amd64.iso
    814M guix-system-install-1.4.0.x86_64-linux.iso
    1.7G nixos-minimal-25.05.813095.1c8ba8d3f763-x86_64-linux.iso
     35M sculpt-25-10.img
    1.1G systemrescue-12.02-amd64.iso
    128K tails-amd64-6.7.img.torrent
    2.0G tails-amd64-7.2.img
    128K ubuntu-mate-22.04-desktop-amd64_archive.torrent
    2.8G ubuntu-mate-22.04-desktop-amd64.iso
    

    p.s. Oops; the provenance of Ventoy is not exactly squeaky clean.

  • genode sculpt on used laptop with linux vm · dckc/madmode-blog #49 (Nov 23, 2025)

    Sculpt 25-10 SSD install: linux required

    Dividing attention with a Chief's game (a downer until they pulled off the win in overtime!), I reminded myself how to install genode on the thinkpad's SSD. After I went through all the trouble to add a connector to the downstairs ethernet cable that was severed in my office move, I discovered that Genode / Sculpt supports wifi on this thinkpad now. Is that new as of 25.10?

    I was surprised to learn that Genode isn't as self-replicating as I'm used to: after I had booted from a USB stick, it's more or less infeasible to set up the internal SSD to boot genode.

    Had to boot linux and copy the boot image from there. (The usual "disk image writer" or just dd works.)

    no progress on setting up a vm

    sculpt-25-10.img (35 MiB)
    SHA256 0530fe9b464e717c1b6114d57893783c00946f3fe53a18721b5560ae1fd247ad

  • diigo unreliable - migrate to Zotero? Hypothesis? · dckc/madmode-blog #32 (Nov 21, 2025)

    Obsidian Web Clipper looks like a contender!

    I don't see support for importing highlights, but the source is open (MIT license).

    Highlighting isn't as convenient as diigo, but then: sometimes having the diigo widget pop up every time I select text is annoying.

  • bib sync: awesome-ocap, Knowledge Representation / Logic / Proof · dckc/madmode-blog #195 (Nov 21, 2025)

    When adding a well-marked blog post using h-entry to Zotero, the primary translator often misses key data.

    In contrast, the Obsidian Web Clipper delighted me with the properties it gleaned:

    Image Image

    I tried it on an item where I'd expect great markup: Thoughts on the Resiliency of Web Projects • Aaron Parecki. It got some false positives: these are authors of comments.

    Image
  • uFork: A pure-actor virtual machine with object-capabilities and memory-safety. · dckc/awesome-ocap #43 (Oct 18, 2025)

    Ah... I see

    EC signature date
    16 November 2022
    Start date
    1 January 2023

  • uFork: A pure-actor virtual machine with object-capabilities and memory-safety. · dckc/awesome-ocap #43 (Oct 18, 2025)

    A project timeline would be in the future.

    I'm talking about recent past events.

    When was the NLnet award announcement?

  • WASI · dckc/awesome-ocap #63 (Oct 16, 2025)

    ah... that's particularly handy, @tarcieri . It pin-points my unease with WASI: I'm 100% fine with Runtime capabilities.

    But references to so-called Link-time capabilities are ambient authority, no?

    The OCap Discipine definition I use includes:

    ... anything globally accessible is immutable data. There is no open(filename) function in the global namespace, nor can such a function be imported.

    and by "global namespace" I also mean module namespaces.

  • uFork: A pure-actor virtual machine with object-capabilities and memory-safety. · dckc/awesome-ocap #43 (Oct 14, 2025)

    how about dated items - blog updates, releases, etc.

    When was the NLNet thing?

  • Android's Binder · dckc/awesome-ocap #61 (Oct 14, 2025)

    It seems that in cases such as Activities, Binder stuff is used to implement capability security.

    But for app permissions, not so much. An LLM summarized it this way:


    The Two-Step Security Model

    The Android system treats the process of getting and using a service like a two-step authentication:

    Step 1: Discovery (The Forgeable Part)

    The client process asks the Service Manager for the Binder object.

    • Request: "Give me the Binder object for 'media.camera'."

    • Result: The Service Manager simply returns the object reference (the IBinder proxy) to any caller. This is intentional; it allows any app to try to use the service.

    • Forgeability Status: Forgeable. You can ask for any service name, and if it exists, you'll get the object. At this point, you have the capability reference, but no actual capability to invoke it.

    Step 2: Enforcement (The Unforgeable Part)

    The client now attempts to call a method on the Binder object (e.g., takePicture()). This is where the security check occurs.

    • Binder Driver Action: When the call hits the kernel, the Binder driver automatically attaches the caller's kernel-level, unforgeable PID/UID credentials to the transaction data.

    • Camera Service Action: The remote Camera Service (the server) receives the transaction, and before executing the method, it calls the enforcePermission() system API.

    • Security Check: The Camera Service asks the core Android security system: "Does the UID attached to this transaction have the android.permission.CAMERA permission?"

    • Unforgeability Status: Unforgeable. The UID is a kernel property of the calling process, assigned at app launch and cannot be manipulated by the unprivileged app.

    In this model, the Binder object reference is not a security token; it's just a routing handle. The UID is the unforgeable security identity.

  • Android's Binder · dckc/awesome-ocap #61 (Oct 14, 2025)

    at the platform level there is another important security model implemented on top of the binder infrastructure. This is the permission/uid-based system, where services can check the uid of incoming calls to verify them against their allowed permissions.

    -- https://stackoverflow.com/a/10590957

  • bib sync: awesome-ocap, Knowledge Representation / Logic / Proof · dckc/madmode-blog #195 (Oct 13, 2025)

    A Microformats Translator for Zotero?

    pls excuse LLM gorp. here's hoping for time to refine this to be more in my own voice

    When adding a well-marked blog post using h-entry to Zotero, the primary translator often misses key data. Zotero favors machine-first standards like COinS and Schema.org (JSON-LD), overlooking the clean, non-redundant nature of microformats. A dedicated translator is the solution to reliably harvest metadata directly from the visible HTML structure.

    Quick-n-Dirty Implementation Sketch

    An 80% solution for a custom Zotero translator only needs simple CSS selectors to pull the key data points directly from the h-entry classes. This avoids the fragility of full HTML scraping while leveraging the non-redundant nature of microformats.

    // Zotero Translator: doWeb function core sketch
    
    var item = new Zotero.Item("blogPost");
    // ... set URL, etc.
    
    // 1. Get the title from p-name
    var titleEl = doc.querySelector('.p-name');
    if (titleEl) {
        item.title = titleEl.textContent.trim();
    }
    
    // 2. Get the author from p-author
    var authorEl = doc.querySelector('.p-author');
    if (authorEl) {
        item.creators.push({
            lastName: authorEl.textContent.trim(),
            creatorType: "author"
        });
    }
    
    // 3. Get the publication date from dt-published
    var dateEl = doc.querySelector('.dt-published');
    if (dateEl) {
        item.date = dateEl.getAttribute('datetime') || dateEl.textContent.trim();
    }
    
    item.complete();
    
    

    The simplicity of using doc.querySelector() for these three fields keeps the build cost negligible. This principle is proven by the successful HTML-to-LaTeX translator I wrote, which uses h-entry as a single source for content conversion. The Zotero development focus on COinS [e.g., See Zotero Translator documentation on COinS priority] is the practical reason this small, custom tool remains necessary.

  • diigo unreliable - migrate to Zotero? Hypothesis? · dckc/madmode-blog #32 (Oct 13, 2025)

    Zotero, Diigo, and Hypothesis: annotation, archiving, tagging

    In a Zotero 7 blog item, I learned it does webpage snapshots. Nice! Cached Pages (Webpage Backup) is one of the main features that keeps me paying for Diigo premium. As noted above, Diigo search flakes out at an alarming rate. Zotero is a mature organization.

    Could Zotero subsume my Diigo usage entirely? Unfortunately, no; the bookmarking / annotation UX is just too klunky for something that I use many times a day, and it's missing support for reverse links:

    Workflow Diigo Zotero
    1. Bookmarking 1. Click Diigo extension. 2. A popup appears to add tags and save. 1. Click Zotero extension to save a snapshot. 2. Open Zotero desktop app to add tags and organize.
    2. Annotation 1. Select text. menu appears 2. Choose highlight 1. Click Zotero extension to save a snapshot. 2. Go to desktop app 3. open the snapshot, 4. ...
    3. Search 1. Click Diigo extension. 2. Open library 3. Enter search terms. 4. anxiously hope it works 1. Open Zotero library in web site or desktop app. 2. Enter search terms. 3. Results appear instantly as I type.
    3. Reverse links automatic. Diigo extension lights up on bookmarked pages. Annotations show up in-context. no notification that the current page is bookmarked in Zotero

    Despite the nice support for annotation standards in Hypothesis, I have yet to get past the kicking-the-tires stage with it.

    Meanwhile, since archiving is what brought on this recent episode, we should look at my archive.org usage too:

    Workflow Diigo Zotero Hypothesis Archive.org
    1. Bookmarking with Tags Delightful Rich, though heavier UX Good? Rich, but even heavier. Creating an item can take minutes
    2. Annotation Delightful 2 gestures Rich, though heavier UX Good? no
    3. Searching Declining Reliability. Excellent once the library is open Good? Fair. Search is for public archives and is not personalized.
    4. Archival Access haven't tried it much Excellent. PDF and web page snapshots Poor. Relies on the live webpage to be available. Excellent. for now.

    For both personal knowledge capture and archival access, Governance / Stability is important:

    Diigo Zotero Hypothesis Archive.org
    Commercial, Private. Blog is offline. Last substantial update was March 2018. Peaked at #13 in a 2011 "Top Tools for Learning" list but not ranked as of 2023, 2024. Non-profit, User-funded. small, paid dev team and a long history of active development. Transitioned from grant funded to user-funded some time before 2020 Non-profit, Grant-funded. Non-profit, Grant-funded. The centralized governance model and the threat of legal challenges (e.g., from publishers) introduce a level of risk to long-term public access.

    Zotero Governance

    About Zotero
    Zotero is a project of Digital Scholar. It was created at the Roy Rosenzweig Center for History and New Media at George Mason University.
    -- credits_and_acknowledgments [Zotero Documentation]

    Handy:

    Diigo UX backed by Zotero storage?

    I have already scripted a tool to export my Diigo history to JSON. Can Zotero's SQLite schema subsume the Diigo JSON format? It has two main parts: item metadata (URL, tags, title) and a nested annotations array.

    1. Metadata: A Straightforward Mapping

    This part is a simple data transformation exercise. Zotero's relational database has clear tables for this data, and Diigo JSON maps cleanly to it.

    • Diigo JSON: url, title, tags, and timestamps (created_at).
    • Zotero SQLite:
      • items table: for the core "Web Page" item type.
      • itemData and itemDataValues: for the URL.
      • itemTags and tags: for a proper relational representation of the tags.
      • dateAdded and dateModified: for the timestamps.
    2. Annotations: Content vs. Position

    The Diigo JSON format for annotations is structured as a simple array of objects. Each object has content (the highlighted text) and comments. The critical missing piece is any positional data—there are no coordinates, no character offsets, nothing that says where on the page the highlight was.

    Zotero's annotation system is built around a local, archived HTML file (the "snapshot"). The annotations in Zotero's database are linked to a specific position within that file. Without the positional data from Diigo, re-creating the highlights in Zotero's reader isn't straightforward.

  • contact link to Agoric discord #dev channel · dckc/madmode-blog #121 (Aug 24, 2025)

    fixed in cce1954

What is Mad Mode?

My day job is writing software to support research at the KU Med Center informatics division, though I'm best known for my work on HTML and Web Architecture at W3C.

I'm a family man, which gives a certain perspective on the KC area, America, and the world we live in.

Between all that, I like to tinker. The bane of my existence is doing things I know the computer could do for me. Have you ever had one of those ideas that won't let go, not even to eat or sleep? My mom said the first time she saw me like that was after they gave me tinker-toys for my 3rd or 4th Christmas. She said I was in "mad scientist mode," just like my father, a chemistry professor.

Mad About...

Acknowledgements