PrivateLets

Software for actually managing rental properties, leases, and tenants. Sign up with your email and get your own copy of PrivateLets; every landlord runs their own instance, so your tenants' data lives with you instead of in somebody else's platform.

  • Aug 12, 2026

    Let Dallas delete an account that never got as far as billing (#30)

    A spam signup or a dead lead can now be deleted from its own page. Deleting appends account.deleted, whose fold rule keeps nothing, so the record drops out of Accounts, AccountByID, AccountByEmail and every other read at once. Nothing is erased and the audit…

  • Aug 12, 2026

    Collapse deliveries into the audit log (#29)

    * Collapse deliveries into the audit log An event already records exactly what was sent, so a second page onto the same data was duplicated machinery: a projection, a Read method, a handler, a route, a policy and its own rendering. The two things the page…

  • Aug 12, 2026

    Stop mailing signups automatically, and screen the form for bots (#28)

    Spam signups were drawing automatic mail out of this domain to whatever address a bot typed in, which costs sending reputation. Two changes: POST /interest no longer mails the visitor. It still creates the account and still texts Dallas. Welcoming someone is…

  • Aug 12, 2026

    Let Dallas delete an email template he no longer wants (#27)

    Deleting appends an email_template.deleted event; the projection drops the record, so the template leaves the index, the account page's send picker, and every read that could edit or send it. Nothing is removed from the log. The interest confirmation is…

  • Aug 12, 2026

    See every email and text this app has tried to send (#26)

    * let dallas see every email and text this app has tried to send Sending happens inline in a request handler with no queue behind it, so the only evidence a message went out was a log line. Every send now writes a delivery.attempted event carrying the…

  • Aug 12, 2026

    Make the audit log scannable again (#24)

    The Data column rendered the whole event payload with break-all and no width constraint, so an email template body wrapped one character per line and a single row grew to 14,000 pixels. Five events, one visible. Each row now shows a clipped one-line preview…

  • Aug 12, 2026

    seed every email template into the log and let dallas send any of them (#25)

    The templates the code sends were a hardcoded map that Read fell back to when a key had no events. Now they are seeded records with well-known IDs. Seeding skips any template that already has an event, so a restart neither duplicates a record nor undoes an…

  • Aug 12, 2026

    let dallas write his own emails, send them by hand, and read the whole event log (#23)

  • Aug 10, 2026

    add a pricing page, practical blog content, and compare tables (#22)

    * add a blog, a tour page, and competitor comparisons for SEO The home page stays what it was — one letter, ending in the sign-up form. Everything new lives on its own pages and only links in from a footer at the very end, after the form, so nothing competes…

  • Aug 9, 2026

    add a blog, a tour page, and competitor comparisons for SEO (#21)

    * add a blog, a tour page, and competitor comparisons for SEO The home page stays what it was — one letter, ending in the sign-up form. Everything new lives on its own pages and only links in from a footer at the very end, after the form, so nothing competes…

  • Aug 8, 2026

    swap the gallery's overlay arrows for prev/next links below it (#20)

    Circular arrow buttons floating on top of the screenshot read as a lightbox control, not part of the page. Previous/Next now sit as plain text links under the frame, with a caption naming the current screen and its position (e.g. "People (4/10)") so it's…

  • Aug 8, 2026

    break the gallery out of the letter's column, and stop the arrows jumping (#19)

    Ten screenshots read as a product tour, and a tour squeezed into a 700px paragraph column looked like an afterthought. The gallery now sits in its own wider block (1100px) between two narrower sections of the letter. The frame height was previously derived…

  • Aug 8, 2026

    Turn the homepage's two screenshots into a full gallery of the product (#18)

    * Turn the homepage's two screenshots into a full gallery of the product Adds eight new fictional Rent mockups (property cards, people, transactions, maintenance, email templates, audit log, and two settings screens) alongside the existing properties table…

  • Aug 8, 2026

    rebrand PrivateLets and rework the homepage (#17)

    Capitalized the brand throughout (Privatelets -> PrivateLets), and redid the marketing letter closer to basecamp.com/about: a house glyph and bigger sans headline (Inter, self-hosted so nothing loads from a font CDN), a rewritten subheadline, no pricing on…

  • Aug 5, 2026

    collapse interest, user and account into one account (#16)

    * say what happened after an action on a lead Send billing, provision and the email edit all redirected back to a page identical to not having clicked, so the only way to know a billing email went out was to go and look in an inbox. Each action now names what…

  • Aug 5, 2026

    say what happened after an action on a lead (#15)

    Send billing, provision and the email edit all redirected back to a page identical to not having clicked, so the only way to know a billing email went out was to go and look in an inbox. Each action now names what it did and the page renders it as a banner.…

  • Aug 5, 2026

    key a landlord by their email, not a phone number (#14)

    A landlord was identified by a mobile number, which meant every admin action on a lead asked Dallas to type one in even though nothing ever texted a landlord — SMS is Dallas's own notification channel and nothing else. An Interest already carries the one…

  • Aug 5, 2026

    fix the volume mount path so interests survive a deploy (#13)

    Every deploy was silently destroying data. Once mounts the persistent volume at /storage (and /rails/storage, mirroring the sibling Rails app), but this Dockerfile wrote the SQLite event log to /app/storage — an ordinary directory baked into the image, never…

  • Aug 5, 2026

    let Dallas edit what the billing email says, and actually send it (#12)

    Two gaps closed together. First, the app never had a real email sender wired up — LogEmail was standing in for it everywhere, including production, so the billing link and signup confirmation were only ever written to the log. A generic SMTP mailer (works…

  • Aug 5, 2026

    let SMS reach nobody but Dallas (#11)

    Provisioning texted the landlord their site's ready. That's the only SMS this app ever sent to someone other than the owner, and it's not needed: Dallas already sees the account in Accounts once it's provisioned, and can follow up himself. SMS now only ever…

  • Aug 5, 2026

    let dallas give each interest its own page, and fix a typo'd email (#10)

    Send billing and provision moved off the interests list and onto a detail page per lead, so the list stays a scan-and-click index instead of a row of inline forms. That page also adds an email update, since a billing link mailed to a typo'd address just…

  • Aug 5, 2026

    let basic auth be the whole of admin authentication, and remove the phone auth it replaced (#9)

    There was no way to reach /admin/* at all: the phone-based signup form that used to live on the homepage was replaced by the /interest email-capture form two PRs ago, and newAdminRequest still needed a real admin User to exist in the domain — one nobody could…

  • Aug 5, 2026

    let /admin and /admin/ land on /admin/accounts (#8)

    Nowhere to go before this: hitting the bare path 404'd. The redirect itself needs no auth check — basicAuth already gates the destination.

  • Aug 5, 2026

    let admin be reachable, gated by basic auth instead of a phone sign-in (#7)

    There was no way to actually reach /admin/* anymore: the phone-based signup form that used to live on the homepage got replaced by the /interest email-capture form, and nothing new took its place. GET /verify needs a pending-signup cookie only POST /signup…

  • Aug 4, 2026

    let mobile browsers actually reflow the page (#6)

    Every screen was missing , dropped when the homepage's head got split out into renderHomePage for the SEO pass. Without it, phones render at a virtual ~980px width and shrink the whole page to fit rather than reflowing text and images — exactly why "the…