• Net Revenue Retention

    NRR measures the ability of a business to expand revenue over time. It’s only really useful if the company sells multiple products or one of them has some sort of usage based scaling factor that implies customers use it more over time.

    One of the misleading ways to look at NRR is when there is a single product that is usage based and scales with the growth of their customers. If the customers growth stops due to an economic condition outside of anyone’s control the NRR becomes stagnant or lower than 100%. All that measures is the growth of the economy rather than the effectiveness of the business.

    See also:


    Published

  • You Can Reach Further Than You Think

    One of the interesting lessons from rock climbing is that people can reach much further than they think they can. When looking at the next hold, it can look so far away that we forget how long our arms and legs actually are. I’m no mountain climber but having gone climbing a few times, this stuck with me.

    In other aspects of life where performance matters a great deal, it’s useful to remember the things that seem out of reach might be within your grasp after all.

    See also:


    Published

  • Personal Log

    I often find myself wondering what I did yesterday. I want to be able to reflect on the day before as I get ready in the morning for the day to come. I also don’t want to make work for myself so the effort has to be useful.

    What would make a personal log useful?

    Facts about the day to jog my memory

    Looking at my calendar to see what meetings I had is a good clue about how my day was spent and who I met with.

    Seeing the tasks I’ve captured and completed also helps.

    Then there are life log things like where I went out to eat and how much time I spent looking at a screen. These are maybe not as useful to look at daily.

    Notes and observations

    I’d like to be better and inspecting my notes and thoughts. I write an enormous about of notes from meetings and tasks, but I don’t review them as readily. Reviewing them more readily would help me turn more of what I’ve learned into permanent notes.

    Things I produced

    Concrete things I’ve produced would be great to catalog to reflect on how it went and what I learned now that it’s complete. This includes writing docs, committing code, shipping a feature, closing a sale, or any other artifact.


    Published

  • How Does Tailscale Work Without Ports Open?

    If a home computer is running on a local network with no ports exposed, how are tools like tailscale working to connect to said computer? How would the computer know that another device is trying to connect to it?

    NAT traversal over UDP.

    Firewalls allow packets from ip:port s where it has observed packets sent to ip:port. A coordination server provides a mapping of devices and ip:port. To get through the Network Address Translation layer (NAT) which rewrites packets (e.g. a home router translating packets from local devices like your phone to come from one IP address on the internet), the STUN protocol informs each device what their ip:port is so they can send packets to the peer device.

    Altogether, by sending packets over UDP and being able to get replies to the correct ip:port, no ports need to be open (though UDP egress is required).

    See also: Why you still need an SSL certificate with tailscale


    Published

  • Dokku, Tailscale, and Letsencrypt

    To set up SSL certificates for use with HTTPs on dokku you can use the letsencrypt plugin.

    sudo dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git

    Next, create an IAM user for dokku-letsencrypt with a custom policy scoped to the hosted zone in Route53. Configure dokku with IAM credentials to pass the DNS-01 challenge:

    dokku letsencrypt:set –global dns-provider route53 dokku letsencrypt:set –global dns-provider-AWS_ACCESS_KEY_ID your_key dokku letsencrypt:set –global dns-provider-AWS_SECRET_ACCESS_KEY your_secret dokku letsencrypt:set –global dns-provider-AWS_REGION aws_region dokku letsencrypt:set –global dns-provider-AWS_HOSTED_ZONE_ID your_hosted_zone dokku letsencrypt:set –global email <your@email.com>

    Enable letsencrypt for the app in dokku:

    dokku letsencrypt:enable <app>

    And set up cron job to auto renew certificates:

    dokku letsencrypt:cron-job –add


    Published

  • Why You Still Need an SSL Certificate With Tailscale

    I have a private network using Tailscale that runs a few local websites and services. Accessing the websites happens via the Tailscale client which connects nodes in the tailnet directly (e.g. my phone and a dokku hosted website) encrypting data from end to end. While this is a great way to secure the session it’s not validating the identity of the website.

    Why does that matter and why does a certificate help?

    DNS can get spoofed and someone on the network you are connecting through could serve the same domain pointing to a malicious website. While unlikely, that means someone could trick you into sharing information you thought was happening on your private website like credentials, document uploads, or photos, or anything else you might normally interact with or share.

    An SSL certificate validates the identity of the private website so that you would receive a browser warning if it was being spoofed.


    Published

  • Fragility Is the Acceleration of Harm

    The definition of fragility (and its inverse antifragility) is the acceleration of harm. For example, if you plot speed of a glass cup hitting the floor and amount of harm to it, the curve rapidly accelerates as the speed goes up. Fragile things are harmed by disorder and stress.

    Therefore the definition of antifragility is the opposite—things that improve as disorder and stress grows. For example, natural selection results in an ecosystem that is more resilient as disorder increases (more species, more variability resulting in better fitness, etc.).

    See also:

    • Thinking in systems shows how multiple stocks has a stabilizing effect on the overall system
    • Antifragile systems exhibit a compounding effect for example, open source software that exponentially increases in popularity/value as it adds more contributors

    Published

  • Using Github Actions to Access Tailnet

    I want to access a private network behind Tailscale network so that I can make an API call to update my personal indexing service when a GitHub repo changes.

    I could use webhooks but I’ve set up Dokku on AWS to be completely private with no ports opened. Supporting webhooks would mean punching a hole in the network for the public internet. (Which could be done with Tailscale Funnel but that’s for later).

    To get notified on changes, I made a workflow in the repo that uses the Tailscale GitHub action.

    1. Create an oauth key with only write permission on the devices category from a tag specified in the workflow step (tag:ci in my case)
    2. Add the oauth client ID and key to the GitHub repo’s Action secrets so it can be made available to the runner
    3. Create a GitHub actions workflow and add a step for setting up Tailscale
    4. Add a step to curl the API in the private tailnet

    Example workflow:

    name: Notify
    
    on:
      push:
        branches:
          - main
    
    jobs:
      notify-index:
        runs-on: ubuntu-latest
        steps:
        - name: Tailscale
          uses: tailscale/github-action@v2
          with:
            oauth-client-id: ${{ secrets.TAILSCALE_OAUTH_CLIENT_ID }}
            oauth-secret: ${{ secrets.TAILSCALE_OAUTH_SECRET }}
            tags: tag:ci
        - name: Call the private API
          id: call_api
          run: |
            #!/bin/bash
            curl -X POST http://my-private-api.com/do-something        
    

    Published

  • Mañana

    “Mañana, a lovely word and one that probably means heaven.”

    I love this line from On The Road by Jack Kerouac. It talks about a small group of friends who spend their day scraping by in the farmlands of California. Everything is pushed to tomorrow as they spend most of their time drinking.

    It perfectly captures that small bit of comfort we get from procrastination.


    Published

  • Dokku on Aws

    I’m setting up dokku as a personal infrastructure PaaS for running services like the personal indexing service.

    This was a confusing affair so I’m writing these notes to reference later if I ever need to set it up again.

    Notes:

    Create an EC2 instance and setup dokku

    • Has to be 2gb or more to avoid issues with dokku installation
    • Has to be Ubuntu (the default Amazon Linux distro will not work)
    • When you ssh into the newly created instance, you have to use the ubuntu default user ubuntu@ec2-address-here.region.compute.amazonaws.com
    • Make sure to add the .pem key to ssh-agent on your local machine or git push dokku main won’t succeed
    • Set up a domain by running dokku domains:set-global mydomain.com and setting up a Route53 CNAME record to point to the public domain name of the AWS EC2 instance (note: this will break if the EC2 instance is restarted, use an AWS Elastic IP to avoid this)

    Create a dokku app

    1. SSH into the dokku host server and run dokku apps:create my-project
    2. On local run git remote add dokku dokku@mydomain.com:my-project
    3. Push git push dokku main and trigger the build/deploy (this just works if you have a Dockerfile at the root of the project)

    Tailscale

    I followed the Tailscale app connector setup instructions to limit traffic to the dokku domain to my tailnet. That means I’m the only one that can access it and I must have tailscale running on my device to access dokku.

    On the dokku EC2 instance

    • Install tailscale curl -fsSL https://tailscale.com/install.sh | sh
    • Run the app connector sudo tailscale up --advertise-connector --advertise-tags=tag:indexer-app-connector
    • Now traffic to the domain is restricted to only go through tailscale

    Using GitHub deploy keys

    I sometimes need access to a GitHub repo at runtime from an application (e.g. pulling the latest from a repo, making a commit, etc.). GitHub has deploy keys for this (single repo key, read-only by default). Putting secret keys into a docker image would be insecure so instead, we can use dokku volume mounts to make them available to the app that needs it.

    1. Make the directory on the dokku EC2 instance that will become the mounted volume mkdir /var/lib/dokku/data/storage/my-app
    2. Copy or generate the deployment key to the directory that was just made
    3. Mount the volume dokku storage:mount my-app /var/lib/dokku/data/storage/my-app:/storage/path
    4. Access it from the running app under /storage/path

    Published

  • Limitations of IOS Shortcuts

    Shortcuts add a scripting layer on top of iOS (and macOS but I don’t use that) that can be executed across any app or screen. I use this for creating notes, getting a calendar link, and copying snippets from Alfred.

    There are some pretty substantial limitations I’ve had to work around.

    • Inserting text into the currently running app is not allowed. The only way to get text into an input from a shortcut is to copy the results to the clipboard and paste it.
    • Returning to the previous app is not possible so you can’t run a shortcut that opens other apps and then return to the original location. There is not workaround unless you explicitely know where you want to return to.
    • With Siri, you have to pause until the request to run a shortcut is acknowledged so you can’t say “Capture todo, the universe isn’t infinite” you have to say “Capture todo” then wait, then say “the universe isn’t infinite”.

    Published

  • The Minority Rule

    Soft drinks in the US are all kosher. It’s not because the US population keeps kosher but because the majority don’t have a strong preference and a minority are absolutely adherent. As a result, it’s easier for soft drink manufacturers to make everything kosher.

    In this way, a small group can have a large impact on the behavior of a wider population. Are there more examples like this?

    (I heard about this in an interview with Nassim Taleb.)


    Published

  • Coming Back to Rust After 4 Years

    I recently picked up rust for a personal infrastructure project and was amazed at the amount of progress on the language and tooling over the years.

    I was able to get up and running fast. Between rustup and cargo it’s dead simple to get a project set up and there is little to no fragmentation in the ecosystem. No fiddling with different package managers, bundlers, test runners—just one tool I already know. It takes me a full day to figure out how to do something similar with TypeScript and the hellscape that is JavaScript tooling.

    Speaking of tooling, between the rust-analyzer and the compiler, knocking out glue code between a few libraries is super easy. Type inference and lifetime elision seems to have gotten significantly better. Autocomplete and docstrings are pletiful. Compiler errors are still best in class, especially coming from Python and mypy. Altogether, rust nails the airplane test with flying colors.

    I spent some time reading up on new language features. I don’t have a feel for how compile times are yet until I have something non-trivial, but I’m glad is being prioritized. Coming from Rust 2018 edition, some of the stabalizations and changes sound very useful like inline const, additions to prelude, IntoIterator for arrays, and so on. I haven’t looked at async rust to see if it’s any less painful but I’m really trying to avoid that for now.

    There are new libraries that have taken off during my time away which are relevant to my interest. Rowan for parsing. Axum for building web servers on top of Hyper (which is now 1.0!). Jiff for dates. Tantivy as an alternative to Lucene.


    Published

  • What Goes on the Billboard?

    An exercise I learned about from Jeff Weinstein about finding a north star metric goes something like this. Imagine the magical version of a vendor that does what you company does—what do they do? For each one of the things they do, how would they prove it to a customer that doesn’t believe them? Finally, would some of the answers make sense and fit on a billboard?

    Now that magical version of a vendor is actually you. What are the gaps between the idealized version of what people want and what you do today? How would you address the gaps and which of them would make what’s on the billboard better? Of course there are the limitations and constraints to work through but this is your north star metric.


    Published

  • Personal Indexing Service

    As much as I love my emacs setup, I can’t take my laptop with me everywhere and that is my biggest compliant. For me investing in personal infrastructure makes sense as I build more one of one software that improves my life. More specifically, there are ways of searching for information I’ve built up over the years that I’ve come to rely on. To be able to search for information consistently across devices, there needs to be a personal indexing service.

    What would a personal indexing service look like?

    I built a protoype indexer and search UI and you can see a demo here.

    I want to build a personal indexer for different documents stays up-to-date so that I can query it securely in one unified place.

    Querying should be multi-modal, it should combine results from similarity search, full text search with BM25 scoring, exact match, graph relationships, and SQL to that I can always get the best result depending on how I want to find things.

    Querying should be provided via an API that I can query securely. Then it can be used from anywhere—iOS shortcut, emacs, org-ai, and other one of one software.

    Overall, I’ll use the following principles to make decisions about what to build:

    1. Minimize maintenance I don’t have time for TLC, it needs to work and not take any of my attention unless I’m improving it. (e.g. always able to rebuild from source, no npm).
    2. Extensible to more sources I should be able to add new ways of searching without having to redo the whole thing. Similarly, I want to use different methods of search depending on what I’m doing or collate them together into one (e.g. exact search, similarity search, LLM, structured).
    3. Fast as hell I really can’t stand things that are slow that I use constantly. I search for things all the time and I’m very sensitive to milliseconds of latency. Speed is undervalued.

    Sources

    Tier 1

    • Notes
    • Journal
    • Meetings
    • Task lists and project files

    Nice to have

    • Email
    • Calendar

    Index

    Should this all live in postgres?

    • Vector similarity search
    • Trigram full text search
    • Graph relationship search

    Indexer

    Write it in Rust to go super fast and multi-threaded? Speed might matter with the volume of file I/O and then text processing that needs to happen. Is python good enough?

    Tools

    For use with LLMs

    • Pre-process the request for search
    • Search notes using similarity search using a vector DB

    For use with other programs

    • Language server for notes to query for related notes as you go, suggest links, see backlinks, auto tag, grammar check

    Mobile

    • Write a note and link other notes to it by inserting an org-id link
      • Shortcut -> web view -> type -> search API -> select result -> paste link
      • Can a shortcut insert text at point? It might be annoying to have to paste after searching. (Sadly no you can’t due to iOS restrictions).
    • Search for a task and jump to it to work on it

    Speed up org-roam and org-ql

    After adding thousands of notes, it’s very slow to (seconds). I’d like to replace both searches with the indexer so it’s consistent and I can add new features on top like omni search, backlinks, related items.


    Published

  • Founder Mode

    Paul Graham’s essay on founder-mode vs manager-mode is about how the advice to “hire good people and give them room to do their jobs” doesn’t work well for founders in practice. Some of the blame, according to the author, is that professional managers and CEOs are really good at faking it and if founders only talk to their direct reports, they will be ineffective.

    While the essay is light on examples, I recognize this pattern from being a founder and having lived through Stripe’s hyper-ish growth from hundreds to thousands of employees. The principal-agent problem is the hardest thing to mitigate and, at a fast growing company, most of the bad behavior of managers will be covered up by inertia. As a founder it is difficult to keep things moving without getting into the details yourself. Getting into the details can be upsetting for managers and employees who don’t understand why this is actually a good thing (maybe even the only thing founders can do to counteract the principal-agent problem).

    The reason founder-mode is so effective is because they 1) have full context and history of the company in their head 2) care a lot to a degree that borders on obsession and 3) are willing and have the authority to make changes that break systems/processes/rules when they don’t make sense.


    Published