7 Mac Apps for Developers Who Live in the Terminal in 2026

If your workflow looks like cd, vim, git push, repeat — you probably spend 90% of your day in a terminal window. But the best terminal-first developers know that a few well-chosen GUI companions can supercharge that workflow without ever pulling you out of the zone.

Here are 7 Mac apps that pair perfectly with a terminal-heavy development style.

1. Warp

The terminal reimagined for modern developers.

Warp is a Rust-based terminal that adds AI command search, block-based output, and collaborative features without sacrificing speed. If you’ve been using the default Terminal.app or even iTerm2, Warp feels like jumping forward a decade. The input editor alone — with proper cursor movement and autocomplete — makes it worth the switch.

🔗 warp.dev

2. Homebrew

The package manager you already have (but might be underusing).

Every terminal developer has Homebrew installed, but most only use it for basics. Brew bundles (brew bundle dump) let you snapshot your entire CLI toolchain into a Brewfile, making fresh setups take minutes instead of hours. Pair it with brew services to manage background processes like Redis and Postgres without touching launchd directly.

🔗 brew.sh

3. Raycast

A keyboard launcher that replaces half your GUI apps.

Raycast is what happens when you build a launcher for people who hate leaving the keyboard. It handles clipboard history, window management, snippet expansion, and has a growing extension store — all invoked with a hotkey. If you’re a terminal person who resents reaching for the mouse, Raycast bridges the gap between CLI efficiency and the GUI things you still need.

🔗 raycast.com

4. TokenBar

Real-time LLM token tracking in your menu bar.

If you’re running AI-assisted workflows from the terminal — Copilot, Claude Code, API calls via curl — your token usage can spiral fast. TokenBar sits in your menu bar and shows exactly what you’re spending across providers in real time. No dashboards to open, no invoices to decode. One glance and you know where your budget stands. At $5 lifetime, it pays for itself the first time it catches a runaway agent.

🔗 tokenbar.site

5. Monk Mode

Feed-level blocking that keeps you in the terminal.

The biggest enemy of terminal flow isn’t a slow build — it’s “let me just check Twitter real quick.” Monk Mode doesn’t block entire apps or websites. It surgically removes the feed from sites like Twitter, Reddit, YouTube, and LinkedIn while leaving search and direct links intact. You can still look up a Stack Overflow answer without getting sucked into an infinite scroll. For $15 lifetime, it’s the cheapest focus investment you’ll make.

🔗 mac.monk-mode.lifestyle

6. Numi

A text-based calculator that thinks like a developer.

Numi lets you type natural-language math — $4,500 / 12 months, 45% of 128GB, today + 3 weeks — and get instant results in a notepad-style interface. It handles unit conversions, currency, time zones, and variables. For quick back-of-napkin calculations while you’re deep in a terminal session, it’s faster than opening python3 and typing print().

🔗 numi.app

7. CleanShot X

Screenshot and screen recording for sharing terminal output.

When you need to share a terminal error, a cool TUI, or a before/after comparison with your team, CleanShot X is unbeatable. It captures scrolling windows, adds annotations, blurs sensitive data, and copies to clipboard — all from keyboard shortcuts. The built-in OCR also lets you grab text from screenshots of terminal output that someone sent you as an image (we’ve all been there).

🔗 cleanshot.com

Wrapping Up

Living in the terminal doesn’t mean ignoring everything else on your Mac. The best setup is a fast terminal at the center with a few focused, lightweight tools filling in the gaps — launching, calculating, tracking costs, staying focused, and sharing output.

What’s in your terminal-first toolkit? Drop your favorites in the comments 👇

How Apple Watch Ejects Water From Its Speaker (And How to Do It on Any Phone)

Apple Watch has a clever feature called Water Lock. Here’s how it works and how to replicate it on any device.

Water Lock: The Science

When you exit Water Lock mode, the Apple Watch plays a ~165Hz tone through its speaker. This specific frequency causes the tiny speaker diaphragm to oscillate rapidly enough to overcome the surface tension holding water droplets inside the speaker grille.

The result: water gets physically pushed outward through the mesh. You can actually see tiny droplets fly off the watch when this happens.

Why 165Hz?

The 150-200Hz range is the sweet spot because:

  • The wavelength matches typical speaker grille cavity dimensions
  • It creates optimal diaphragm displacement
  • The oscillation is fast enough to break water surface tension
  • But gentle enough not to damage the speaker

Replicating This on Any Phone

The same physics applies to any speaker. A free web tool at fixspeaker.com implements a 30-second frequency sweep from 150-200Hz.

Steps:

  1. Open fixspeaker.com
  2. Turn volume to maximum
  3. Press Eject Water
  4. Hold the device with speaker facing down
  5. Repeat 2-3 times

The Web Audio API Behind It

const ctx = new AudioContext();
const osc = ctx.createOscillator();
osc.type = 'sine';

// Sweep through the effective frequency range
osc.frequency.setValueAtTime(150, ctx.currentTime);
osc.frequency.linearRampToValueAtTime(200, ctx.currentTime + 30);

osc.connect(ctx.destination);
osc.start();
setTimeout(() => osc.stop(), 30000);

The key insight is using a sweep rather than a fixed tone. Different speaker sizes resonate at slightly different frequencies within the 150-200Hz range, so sweeping ensures you hit the optimal frequency for any device.

Results

Tested across iPhone 15, Pixel 8, Samsung S24, MacBook Air:

  • Submersion recovery: 1-2 cycles
  • Rain/splash: 1 cycle
  • Steam/humidity: 2-3 cycles

The next time someone tells you to put your wet phone in rice, show them this instead.

THREE.JS ELEGANCE: INTERACTIVE FLOWER SHADER VOL. 03

Elegance: Interactive Flower Shader

A sophisticated, interactive visual experience blending high-end editorial design with advanced WebGL techniques. This project features a 3D-distorted floral gallery element powered by Three.js and custom GLSL Shaders.

Three.js #GLSL #Shaders

What Temporal Can’t Do: Human Approval Mid-Workflow

Temporal is excellent at durable execution. I’m not here to argue otherwise.

But try adding a human approval step to a Temporal workflow. Something like: agent validates a database migration, then an SRE needs to approve before it runs.

What This Looks Like in Temporal

@workflow.defn
class ApprovalWorkflow:
    def __init__(self):
        self.approved = None

    @workflow.run
    async def run(self, change):
        result = await workflow.execute_activity(
            validate_change, change,
            start_to_close_timeout=timedelta(minutes=5)
        )

        # Wait for human signal
        await workflow.wait_condition(
            lambda: self.approved is not None
        )

        if not self.approved:
            return {"status": "rejected"}

        return await workflow.execute_activity(
            apply_change, change,
            start_to_close_timeout=timedelta(minutes=10)
        )

    @workflow.signal
    async def approval_signal(self, approved: bool):
        self.approved = approved

This is the workflow. Now you need:

  1. A notification service – Temporal doesn’t notify humans. You build email/Slack integration.
  2. A UI or API for the human to send the signal back to Temporal.
  3. Reminder logic – if the human doesn’t respond in 30 minutes, send another notification. Temporal doesn’t do this.
  4. Escalation – if person A doesn’t respond in 2 hours, notify person B. You build this.
  5. A Temporal cluster – deploy, operate, monitor. Or pay for Temporal Cloud.
  6. Determinism constraints – no random(), no datetime.now(), no network calls in workflow code. Every developer on the team needs to learn this.

The workflow is 20 lines. The infrastructure around it is 200.

What This Looks Like Without Temporal

from axme import AxmeClient, AxmeClientConfig

client = AxmeClient(AxmeClientConfig(api_key=os.environ["AXME_API_KEY"]))

intent_id = client.send_intent({
    "intent_type": "intent.infra.change_approval.v1",
    "to_agent": "agent://myorg/production/infra-validator",
    "payload": {
        "change_type": "database_migration",
        "target": "prod-postgres-main",
        "migration": "add_index_users_email",
    },
})
result = client.wait_for(intent_id)

Four lines. The platform handles:

  • Durable execution (state in PostgreSQL)
  • Human approval gate (built-in, not bolted on)
  • Reminders (configurable: 5 min, 30 min, custom)
  • Escalation (person A -> person B -> team)
  • Timeout (graceful, with fallback action)
  • Notification (CLI, email, Slack)
  • Audit trail (who approved, when, with what context)
  • No determinism constraints
  • No cluster to operate

Side-by-Side

Temporal AXME
Lines of code 80+ 4
Human approval Build it (signals + UI + notifications) Built-in
Reminders Build it Built-in
Determinism constraints Required None
Infrastructure Cluster (self-hosted or Cloud) Managed API
Setup time Days Minutes
Escalation Build it Built-in

When to Use Which

Use Temporal when:

  • You have complex, long-running workflows with many branches and compensation logic
  • You have a platform team that can operate a Temporal cluster
  • Determinism constraints are acceptable for your team
  • Human approval is a small part of a larger workflow

Use AXME when:

  • You need human approval gates in otherwise simple workflows
  • You don’t want to operate infrastructure
  • Your team doesn’t want to learn determinism constraints
  • You need built-in reminders, escalation, and structured approval types
  • You need it working in minutes, not days

They’re not competing for the same use cases. Temporal is a workflow engine. AXME is a coordination layer with human approval built in.

Try It

Working example – agent validates an infrastructure change, workflow pauses for SRE approval, completes after human decision:

github.com/AxmeAI/durable-execution-with-human-approval

Python, TypeScript, and Go implementations included.

Built with AXME – durable execution with human approval built in. Alpha – feedback welcome.

Wrapping Up Ekehi: The Code, The Team, and What I Am Taking With Me

Ekehi, meaning “sunrise” or “discovery”, is an open-source business resource platform built for women entrepreneurs in Nigeria and across Africa. The problem it solves is real: funding exists, grants exist, credit schemes and training programmes exist, but finding them is genuinely hard, especially if you are not already connected to the right people. Ekehi puts all of that in one place. A searchable, filterable directory of funding opportunities, credit products, and training resources, built mobile-first for the women who need it most.

The platform is live at ekehi.netlify.app and the full codebase is open source. Built by 15 people across four sprints. See it here: Ekehi on GitHub

This is my final sprint entry. Four weeks, three previous articles, and a lot of lessons.

The Product and Why It Exists

Ekehi is a business intelligence platform designed to bridge the gap between women entrepreneurs and limited access to capital, professional networks, and visibility. It serves as a searchable, filterable hub that connects women-led SMEs with active grants, credit schemes, and growth training, all in one place.

The stack the team was assigned to build with is Vanilla HTML, CSS, and JavaScript on the frontend with Node.js and Express.js on the backend and Supabase handling the database and authentication. For many of the women this platform serves, mobile is the primary device and connectivity is not always reliable. Every technical decision on this project sits inside that context.

The choice of purple as a primary colour was intentional. By aligning with the global identity of International Women’s Day, the platform signals immediate trust and dignity to the women it is built for.

The Journey So Far

I published articles covering my work in each sprint. Here is the quick version:

Sprint 1 — Built the Mission section on the landing page. The technical challenge was positioning four decorative SVG petal icons around the eyebrow label using absolute positioning rather than flexbox, which kept them from disrupting the layout flow.

Sprint 2 — Built the filter section on the Opportunities page. This was the most layered task I worked on across the whole sprint. ES module imports, a shared component system, CSS scoping, JavaScript wiring, and a filter state object all in one task.

Sprint 3 — Refactored the What We Offer section into a two-column interactive layout with CSS Grid and JavaScript click handlers for item activation.

Each of those sprints has a full write-up. This article covers Sprint 4 and wraps up the full journey.

What I Shipped This Week

My final task was building the guide detail page at client/resources/guides/detail/. This is the reading page that renders a single guide’s full content when a user navigates from the resources page.

The HTML Structure

Before writing any CSS or JavaScript, the HTML structure came first. The page has two main areas: a sidebar on the left and an article on the right. The semantic element choices were deliberate:

<main class="container">
  <div class="guide">
    <aside class="guide__sidebar">
      <a href="/resources/" class="guide__back">← Go back</a>
      <nav class="guide__toc" aria-label="Table of contents">
        <div id="toc-root"></div>
      </nav>
    </aside>
    <article class="guide__content">
      <h1 class="guide__title" id="guide-title"></h1>
      <div class="guide__body" id="guide-body"></div>
    </article>
  </div>
</main>

<aside> is the correct semantic tag for secondary content that supports the main content. <nav> inside the sidebar is correct because the table of contents is navigation within the page. <article> is correct for the main content because a guide is a self-contained piece of long-form content that makes sense on its own.

The id="toc-root" and id="guide-body" divs are empty mounting points. JavaScript fills them with content at runtime.

The Layout

The two-column layout uses CSS Grid with 250px 1fr rather than 1fr 1fr:

.guide {
  display: grid;
  grid-template-columns: 250px 1fr;
  gap: var(--space-16);
  padding-block: var(--space-10);
  align-items: start;
}

The sidebar has a predictable fixed width since it just holds a list of links. The content area should take all the remaining space since it holds long-form article text. Equal columns would waste space on the sidebar and squish the content. align-items: start keeps the sidebar from stretching to match the full height of the article.

The sidebar is sticky so it stays visible as the user scrolls through the long content without moving with the page.

The Routing

There is no API endpoint for guides yet so the page uses hardcoded dummy data. The routing reads a guideId from the URL using URLSearchParams:

const params = new URLSearchParams(window.location.search);
const guideId = params.get("guideId");

A URL like /resources/guides/detail/index.html?guideId=1 gives guideId the value "1". The guides object is keyed with string keys to match. If the guideId is missing or does not match anything in the data, the page shows a friendly error state instead of breaking.

The Collapsible Table of Contents

The issue specifically required a collapsible table of contents. Each section heading is a button that toggles its sub-items open and closed using aria-expanded:

headingBtn.addEventListener("click", () => {
  const isExpanded = headingBtn.getAttribute("aria-expanded") === "true";
  headingBtn.setAttribute("aria-expanded", String(!isExpanded));
  itemList.style.display = isExpanded ? "none" : "flex";
});

aria-expanded tells screen readers whether the section is open or closed so keyboard and assistive technology users know the state of the TOC without having to guess.

The TOC items are <button> elements rather than <span> elements. This came up as a PR review comment from GitHub Copilot. Spans are not keyboard focusable by default which means keyboard users could not interact with the TOC. Buttons are focusable natively and behave correctly without any extra work.

The active item gets a tilde prefix through CSS alone with no extra JavaScript needed:

.guide__toc-item--active::before {
  content: "~ ";
}

Responsive Behaviour

On mobile the table of contents is hidden entirely and the layout collapses to a single column. The decision to hide rather than collapse the TOC was deliberate. On a small screen a long list of navigation links before the article content creates friction. The reader just wants to get to the content. The Go back link stays visible since it sits outside the TOC as a sibling element, not inside it.

Full Sprint Retrospective

What went well was the tooling and the structure. GitHub Issues, ClickUp, and the branching strategy kept the project moving even when things got messy. Having clear acceptance criteria on every issue meant you always knew what done looked like. The team leads showing up and keeping momentum going made the difference in weeks where things could have stalled.

What surprised me was how much communication, or the lack of it, can affect a whole team. People not showing up to tasks, going quiet when help is needed, and being unwilling to step outside their assigned role even when the whole team might suffer for it. That was hard to watch and harder to manage. Leadership also surprised me. It is not something you prepare for. You just have to stand up and take the lead even when others are not following, and figure it out as you go.

What I would do differently is take up more responsibility earlier. I held back in some areas where I could have stepped in and I think the team would have benefited if I had been more proactive from the start. I would also push harder on communication from week one rather than trying to manage the gaps later.

What I am most proud of is the filter section on the Opportunities page from Sprint 2. It was the most layered task I worked on across the whole sprint. ES module imports, a shared component system, CSS scoping, JavaScript wiring, and a filter state object all in one task. Working through a conflict between what the issue specified and what the codebase actually supported at the time, methodically rather than panicking, was a personal win for me.

Key Technical Lessons

CSS Grid vs Flexbox. I went into this project knowing Flexbox but not really knowing when to reach for Grid. By the end I understood it clearly. Flexbox for one-dimensional layouts, Grid for two-dimensional ones. Every two-column layout I built across the sprint used Grid because both columns needed to be aware of each other vertically.

Working within a design system. Every value traces back to a CSS custom property. No hardcoded colours or values. This discipline took getting used to but I understand now why it matters. Consistency across a codebase with 15 contributors would fall apart without it.

CSS scoping. When a component renders its own HTML at runtime through JavaScript, you cannot add classes to its internal elements directly in your HTML. You scope your styles through a parent BEM class you control. This keeps your overrides contained to your section and leaves the shared component untouched everywhere else.

Semantic HTML is not optional. Choosing the right HTML element matters. <aside> for sidebar content, <nav> for navigation lists, <article> for self-contained content, <button> for anything interactive. These choices affect accessibility, screen reader behaviour, and keyboard navigation.

Reading someone else’s code before touching it. Refactoring the What We Offer section in Sprint 3 taught me to slow down before writing anything. Read first, understand the structure, then make changes. Jumping in too quickly causes more problems than it solves.

Git as a collaboration tool. Forking, upstream syncing, pull requests, merge conflicts, ES module imports. I came into this project as a beginner and the git workflow alone taught me more than I expected. One merged PR on a real open source project teaches you more than ten personal projects built alone.

What Is Next

I plan to strengthen my JavaScript fundamentals properly before moving on. After that, TypeScript is the next step. I also want to start learning how to integrate data structures and algorithms into writing better, more efficient code. This sprint showed me that knowing how to write code is one thing. Writing code that is clean, scalable, and makes sense to someone else is entirely another.

Meet The Team

Frontend Track
Marionbraide — [email protected]
Osezele Iboi — [email protected]
Esther Orieji — [email protected]
Iyobosa — [email protected]
Oluchi Okwuosa — [email protected]
Victor Okoukoni — [email protected]
Florence Onwuegbuzie — [email protected]
Fatihat — [email protected]

UI/UX Track
Michael Babjide Boluwatife
Fisayo Rotibi
Osuji Wisdom

Mobile Track
Gabriel Abubakar — Contributing as QA
Jesse Nwaokolo — Contributing to frontend

Backend Track
Olusegun Adeleke
Sodiq Semiu

TEE Foundation | Tabi Project | International Women’s Day Initiative | March 2026

L’email comme une app. Pas comme un message.

Interface minimaliste d

Interface minimaliste d'une application sur mobile

La plupart des gens pensent l’email comme un texte avec un bouton à la fin. Un message qui dit quelque chose, et un appel à l’action qui espère un clic.

Je le pense autrement. Un email est une interface. Il a des états. Il a une navigation. Il a une logique de flux. Et surtout — il a un seul objectif : amener l’utilisateur à l’action suivante avec le moins de friction possible.

Ce changement de perspective change tout dans la façon de concevoir ses emails.

L’email comme premier écran

Quand une médiathèque reçoit mon mail de prospection, elle ne reçoit pas un texte. Elle reçoit un écran avec deux actions possibles :

  • Oui, envoyez-moi un devis → elle entre dans le tunnel de vente
  • Non merci → elle entre dans le tunnel de feedback

C’est exactement la logique d’un écran d’onboarding dans une application mobile. Une question. Deux chemins. Zéro bruit parasite.

La différence avec un email classique : je ne lui demande pas de répondre. Je lui demande de cliquer. Répondre demande de la rédaction, de la réflexion, de l’effort. Cliquer demande une demi-seconde et un index.

La friction tue la conversion. Chaque étape supplémentaire entre l’intention et l’action coûte des utilisateurs.

C’est vrai dans une app. C’est vrai dans un email.

Les boutons comme navigation

Dans une application, les boutons naviguent vers des écrans. Dans mon email de devis, c’est exactement ce qui se passe — chaque bouton amène vers une page dédiée, contextualisée, avec les bonnes informations au bon moment.

Le mail de devis contient quatre actions distinctes :

1. Voir les thématiques → page publique de mon site de conférences. Pour le responsable culturel qui veut affiner le sujet avant de s’engager.

2. Proposer des dates → formulaire de disponibilités pré-rempli avec le numéro de devis. Pour celui qui est convaincu et veut passer à la planification.

3. Signer le devis en ligne → page de signature électronique avec canvas HTML5. Pour celui qui est prêt à valider.

4. Transmettre à ma direction → lien mailto: pré-rempli avec le sujet, le corps du mail, et le lien vers le devis. Pour celui qui n’a pas le pouvoir de signature mais veut faire suivre.

Ce dernier bouton est souvent le plus sous-estimé. Dans une structure culturelle publique, la personne qui reçoit le mail n’est presque jamais celle qui signe. En lui donnant un bouton “transmettre à ma direction” avec un mail pré-rédigé, je supprime une étape entière du processus décisionnel — et je m’assure que le bon message arrive au bon interlocuteur, formulé correctement, sans que mon contact ait à rédiger quoi que ce soit.

mailto:?subject=Proposition d'intervention : Démystifier l'IA
&body=Bonjour,

Je vous transmets ce projet de conférence sur l'IA.
Vous trouverez le récapitulatif et le devis ici : [lien]

Qu'en pensez-vous ?

Un bouton. Un mail pré-rédigé. Zéro effort pour mon contact.

Le kit de communication comme feature

Une app bien conçue anticipe les besoins suivants de l’utilisateur. Quand quelqu’un accepte un devis de conférence, son besoin suivant c’est : communiquer sur l’événement. Affiche, texte de présentation, codes couleurs pour les visuels.

J’aurais pu attendre qu’il me les demande. J’aurais pu les envoyer dans un second mail. J’ai choisi de les inclure directement dans le mail du devis — parce que c’est là qu’il en a besoin, au moment où il vient de décider de signer.

Le mail contient donc :

  • Le PDF du devis
  • Ma photo de profil
  • Une affiche PNG haute définition
  • Deux textes de présentation prêts à copier (version courte et longue)
  • Les codes couleurs de la charte graphique
  • La police typographique utilisée (Montserrat, Google Fonts)

Ce n’est pas un gadget. C’est une réduction de friction. Chaque élément que je fournis proactivement est une relance que je n’aurai pas à gérer, une question à laquelle je n’aurai pas à répondre, un délai que je supprime dans le processus de décision.

Le délai aléatoire comme design d’expérience

Un devis qui arrive 9 secondes après un clic casse l’illusion. Le destinataire comprend immédiatement qu’il est dans un tunnel automatisé — et ça change sa perception de la relation.

J’ai introduit un délai aléatoire entre 3 et 8 minutes entre le clic et l’envoi du devis. C’est suffisant pour que ça ressemble à quelqu’un qui a pris le temps de préparer le document. Pas trop long pour créer de l’impatience.

$delay = rand(180, 480); // 3 à 8 minutes
$sendAfter = date('Y-m-d H:i:s', strtotime('+' . $delay . ' seconds'));

C’est du design d’expérience appliqué à un email automatisé. Exactement comme un skeleton screen dans une app qui simule un chargement pour que l’interface paraisse plus réactive — même si les données sont déjà là.

L’objectif n’est pas de tromper. C’est d’éviter que la mécanique soit visible. Une bonne automatisation ne devrait pas ressembler à une automatisation.

Ce que ça implique dans la conception

Penser l’email comme une app, ça change la façon de le construire :

On ne rédige plus, on conçoit. La question n’est plus “qu’est-ce que je veux dire ?” mais “quelle action je veux déclencher, et comment je supprime les obstacles entre l’intention et l’action ?”

Chaque bouton est un écran suivant. Avant d’ajouter un CTA, je me demande vers quoi il pointe, ce que l’utilisateur trouvera là, et si cette page est prête à le recevoir dans le bon contexte.

On anticipe le besoin suivant. Un utilisateur qui vient de signer a besoin d’assets de communication. Un utilisateur qui vient de s’inscrire a besoin d’instructions claires. Ces besoins sont prévisibles — autant les satisfaire proactivement plutôt que de les gérer en réactif.

La friction est l’ennemi. Chaque champ de formulaire supplémentaire, chaque étape de confirmation, chaque mail qu’on leur demande de rédiger eux-mêmes — c’est une fuite dans le tunnel. On colmate les fuites avant de lancer la campagne.

L’email n’est pas un canal. C’est une surface.

Les grandes apps ont des surfaces : l’écran principal, les notifications push, les widgets. L’email est une surface comme les autres — avec ses contraintes propres (pas de JavaScript, clients mail capricieux, attention limitée) mais aussi ses avantages uniques : il arrive dans un espace personnel, il persiste, il peut être transféré, il peut contenir des pièces jointes.

La plupart des emails d’entreprise traitent cette surface comme un Post-it. Un texte, un lien, une signature. J’essaie de la traiter comme un écran — avec une architecture d’information, une hiérarchie visuelle, et une logique de flux pensée pour l’utilisateur qui est de l’autre côté.

Ce n’est pas plus compliqué. C’est juste une question de perspective.

How Commit–Reveal Makes a 50-50 Coin Flip Actually Fair on Base

Everyone in crypto says “provably fair”. Fewer people are willing to walk through the exact mechanics.

This is how we implemented a true 50-50 P2P USDC coin flip on Base using a simple commit–reveal pattern, zero rake, and non-custodial smart contracts:

1. The design constraints

We started with a few hard rules:

  • No house edge or rake. Winner takes 100% of the pot.
  • Yoss.gg never holds user funds in a custodial wallet.
  • Every flip must be independently verifiable on-chain.
  • UX has to work for someone who has never touched a wallet before.

Those constraints immediately kill most “crypto casino” architectures. So we built a minimal primitive instead: one contract, one coin, one flip.

2. Why commit–reveal and not just blockhash

Using blockhash or timestamps for randomness looks simple but is trivial to manipulate—especially for miners/validators and contracts that can choose when to settle.

Commit–reveal gives you:

  • A private secret at commit time
  • A public commitment stored on-chain
  • A reveal step that proves you did not change your secret after seeing the other side

In our case, each player contributes entropy and the contract combines both secrets so neither side can force a win alone.

3. A minimal flow for a coin flip

At a high level:

  1. Player A chooses a random 256-bit secret sA and computes hA = keccak256(sA).
  2. Player A posts a game with stake X USDC and commitment hA. Funds go into the contract.
  3. Player B matches the game with stake X USDC and their own commitment hB.
  4. Once both commitments are locked, each player reveals their secret (sA, sB).
  5. The contract verifies keccak256(sA) == hA and keccak256(sB) == hB.
  6. We derive randomness from keccak256(sA, sB) and take the least significant bit as the coin flip.
  7. Winner receives the full 2X USDC pot.

No oracle, no trusted server, no off-chain “magic RNG”. If either player refuses to reveal, the contract has explicit timeout logic for refunds/penalties depending on who stalled.

4. Non-custodial by default

All funds live in the contract, never in a Yoss.gg wallet. We use USDC on Base for:

  • Stable, non-volatile stakes
  • Cheap gas
  • Fast settlement

The contract enforces:

  • Exact stakes from both sides
  • No skimmed fees or silent rake
  • Direct payouts to the players’ smart accounts

Because there is no house edge, there is nothing for us to siphon off even if we wanted to. The pot flows from Player A + Player B straight to the winner.

5. Making this usable for non-crypto players

Everything above is great for devs and fairness nerds. Most players just want to click a button, flip a coin, and see numbers go up or down.

To bridge that gap we:

  • Use smart accounts (ERC-4337) so users can sign up with email instead of a browser wallet.
  • Abstract gas on Base so they are not juggling ETH balances.
  • Integrate fiat on-ramps (MoonPay, Coinbase, Transak) so they can land directly in USDC on Base.

Under the hood, they are still using the same non-custodial contract and commit–reveal flow. They just never have to think about it unless they want to audit the chain.

6. How you can verify a flip yourself

If you are the kind of person who reads commit–reveal explainers (hi, friend), verification is straightforward:

  1. Look up the game and its commitments on Base.
  2. Fetch the revealed secrets from the resolution transaction.
  3. Locally compute keccak256(sA) and keccak256(sB) and confirm they match the commitments.
  4. Compute keccak256(sA, sB) yourself and check that the least significant bit matches the winner recorded on-chain.

If any of those checks fail, something is wrong—and you do not have to take our word for it.

7. Why we care about boring math

Crypto is full of “provably fair” banners glued onto opaque systems. We went the opposite way: minimal game, maximal transparency.

A single coin, zero rake, and a commit–reveal you can verify in a 20-line script.

If more games looked like this, we would have fewer rug stories and more people willing to touch on-chain betting at all.

If you are building something similar on Base (or want to), I would love to see your approach and tradeoffs.

J’ai automatisé tout mon cycle de vente. Voilà ce qui s’est passé.

Un bureau épuré avec un ordinateur portable ouvert

Un bureau épuré avec un ordinateur portable ouvert

Il y a quelques semaines, j’ai envoyé 456 emails à des médiathèques et centres culturels pour proposer ma conférence Démystifier l’IA. Je n’ai pas passé 900 minutes à les rédiger. Je n’ai pas non plus passé une seule minute à générer les devis qui ont suivi.

Résultat : 38 demandes de devis en 4 jours. 8,67% de taux de réponse sur un public froid, sans aucune relation préalable.

Ce chiffre ne tombe pas du ciel. Il est le produit d’un flow entièrement automatisé — du premier email jusqu’à la signature électronique du contrat. Voilà comment ça marche, pourquoi ça marche, et ce que j’ai appris en le construisant.

Le problème que je voulais résoudre

Je suis développeur indépendant. Je fais des conférences sur l’IA. Entre les deux, il y a un cycle de vente que la plupart des indépendants gèrent à la main :

  1. Trouver les contacts
  2. Envoyer un email
  3. Attendre
  4. Relancer
  5. Envoyer un devis en PDF
  6. Attendre encore
  7. Relancer encore
  8. Recevoir le bon pour accord par fax (oui, en 2026)

C’est épuisant, chronophage, et surtout — c’est du temps que je ne passe pas à coder ou à préparer mes conférences. J’ai décidé d’automatiser les étapes 2 à 8. Entièrement.

Le flow, étape par étape

1. La liste de contacts

Tout commence avec un fichier CSV : médiathèques.csv. Nom de la structure, email, adresse, code postal, ville.

Un script Python filtre les emails invalides, exclut les zones trop éloignées (déplacements trop coûteux), et nettoie les noms de structures — parce qu’une médiathèque qui s’appelle "Médiathèque intercommunale du Pays de l'Arbresle (MIPA)" dans un CSV ne mérite pas d’apparaître telle quelle dans un email.

def clean_library_name(name):
    name = re.split(r'.|,', name)[0]
    name = name.replace('"', '').strip()
    return name if len(name) > 2 else 'Médiathèque'

2. Le calcul des frais de déplacement

C’est le détail qui change tout : chaque devis intègre automatiquement les vrais frais kilométriques, calculés à la volée.

Le script géocode l’adresse de la structure via l’API gouvernementale api-adresse.data.gouv.fr, puis calcule la distance aller-retour via OSRM (un routeur open source). Si la structure est à moins de 30 km, les frais sont à zéro. Au-delà, le tarif kilométrique s’applique.

distance_km = data["routes"][0]["distance"] / 1000
frais = round(distance_km * 2 * tarif, 2) if distance_km >= SEUIL_KM else 0.0

Résultat : une médiathèque à 15 km reçoit un devis à 300 €. Une autre à 80 km reçoit un devis à 324 €. Automatiquement. Sans que j’aie tapé un seul chiffre.

3. L’email initial

Le mail envoyé via Brevo est volontairement court. Une seule question. Deux boutons.

Seriez-vous intéressé de recevoir un devis pour une conférence sur l’intelligence artificielle dans votre structure ?

[Oui, envoyez-moi un devis] · [Non merci]

Pas de présentation interminable. Pas de liste à puces. Juste une décision binaire à prendre.

Le bouton “Non merci” est aussi important que le “Oui” — il redirige vers un formulaire de refus qui me donne les vraies raisons (budget, timing, pas intéressé…) et propose de rester dans ma liste ou d’en sortir. Je collecte de la donnée même sur les non.

4. La génération du devis

Quand quelqu’un clique sur “Oui”, un token unique est créé côté serveur. Ce token déclenche :

  • La création d’un client en base de données (ou la mise à jour s’il existe déjà)
  • La génération d’un devis numéroté (DEV-202603-XXXX)
  • La génération d’un PDF via TCPDF
  • La mise en file d’attente de l’email (email_queue)

Le devis ne part pas immédiatement. Un délai aléatoire entre 3 et 8 minutes est introduit volontairement — parce qu’un devis qui arrive 11 secondes après un clic, ça fait robot. Un devis qui arrive 5 minutes après, ça ressemble à quelqu’un qui a pris le temps de le préparer.

$delay = rand(180, 480); // entre 3 et 8 minutes
$sendAfter = date('Y-m-d H:i:s', strtotime('+' . $delay . ' seconds'));

5. L’email du devis

Le devis PDF est joint à l’email, mais l’email lui-même est pensé comme un kit complet :

  • Le PDF du devis
  • Ma photo de profil
  • Une affiche PNG haute définition pour les communications
  • Les textes prêts à copier-coller pour les flyers et réseaux sociaux
  • Les codes couleurs de la charte graphique
  • Trois boutons d’action : voir les thématiques, proposer des dates, signer en ligne

L’idée : zéro friction. La structure reçoit tout ce dont elle a besoin pour dire oui et pour communiquer sur l’événement. Elle n’a pas à me relancer pour un visuel.

6. La signature électronique

Le devis contient un lien vers une page de signature en ligne. Le client dessine sa signature sur un canvas HTML5, clique sur “Bon pour accord”, et :

  • La signature est horodatée avec l’IP
  • Un PDF final est regénéré avec la signature incrustée
  • Le client reçoit son exemplaire signé par email
  • Je reçois une notification avec un lien vers le CRM
const signaturePad = new SignaturePad(canvas, {
  backgroundColor: "rgba(255, 255, 255, 0)",
  penColor: "#1a1a1a",
});

Tout ça sans que j’intervienne une seule fois.

Les chiffres

Étape Volume
Emails envoyés 900
Demandes de devis 47 (5,2%)
Devis générés automatiquement 47
Minutes passées manuellement 0

Pour contextualiser : la moyenne B2B du cold email en 2025 tourne autour de 4% de taux de réponse. Les meilleures campagnes atteignent 10%. Un taux de réponse de 8,67% sur un public entièrement froid, c’est dans le top 10% mondial des campagnes cold email — surtout pour un indépendant sans équipe commerciale.

Ce que j’ai appris

Le bouton “Non merci” est sous-estimé. La plupart des campagnes de cold mail ignorent les refus. Moi je les collecte, je les analyse, et je m’en sers pour améliorer le ciblage. Un refus pour raison budgétaire n’est pas la même chose qu’un refus par désintérêt.

Le délai aléatoire change la perception. Un email qui arrive trop vite casse l’illusion. Trois à huit minutes de délai, c’est suffisant pour que ça ressemble à une interaction humaine.

Donner plus que ce qu’on demande. Envoyer le devis ET le kit de communication ET les textes prêts à l’emploi, c’est retirer une étape au client. Moins il doit faire, plus il avance vite vers la signature.

Automatiser ne veut pas dire déshumaniser. Le mail initial est court, direct, non-corporate. Il n’a pas l’air d’un mail automatisé parce que je n’ai pas essayé de le rendre impressionnant — j’ai essayé de le rendre honnête.

La stack

  • Python — script d’envoi, géocodage, calcul de distance (OSRM)
  • Brevo — envoi transactionnel
  • PHP — backend CRM, génération de devis, API
  • TCPDF — génération PDF
  • MySQL — clients, devis, queue d’emails
  • Signature Pad (JS) — signature électronique côté client

Tout le code tourne sur un hébergement mutualisé standard. Pas de serverless, pas de microservices, pas de Kubernetes. Un VHost PHP et une base MySQL suffisent.

Si tu es indépendant et que tu passes encore du temps à envoyer des devis manuellement, je t’encourage à regarder ce que tu peux automatiser. Pas tout d’un coup — une étape à la fois. Commence par le devis. Puis la relance. Puis la signature.

Le temps que tu récupères, tu peux le mettre ailleurs. Dans le travail que tu aimes. Ou dans un article de blog.

PostgreSQL Streaming WAL Archiver and a backup tool in Go (pgrwl)

A production-grade, cloud-native PostgreSQL WAL archiving system designed for:

  • streaming WAL to S3 with compression, encryption, and retention
  • Kubernetes-native PostgreSQL backup workflows
  • zero data loss and reliable Point-in-Time Recovery (PITR)

Project

https://github.com/hashmap-kz/pgrwl

Features

  • WAL receiver (replication protocol)
  • Continuous WAL streaming
  • Backup to S3 (MinIO, AWS, etc.)
  • Backup to SFTP (backup server)
  • WAL compression (gzip, zstd-ready)
  • WAL encryption (AES-GCM)
  • WAL retention management
  • WAL monitoring and observability
  • Kubernetes & container ready
  • Helm chart support
  • YAML / JSON / ENV config
  • Lightweight single binary
  • Structured logging
  • Integration tests (containerized)
  • Unit tests
  • Backup automation (streaming basebackup)
  • Continuous backup for PostgreSQL

Key Capabilities

Streaming WAL

  • Uses PostgreSQL replication protocol
  • Supports synchronous WAL streaming
  • Enables zero data loss setups

Storage Backends

  • S3-compatible storage
  • SFTP backup servers

Compression + Encryption

Pipeline based on filename:

000000010000000000000001.gz.aes

Flow:

  • compress -> encrypt -> upload
  • download -> decrypt -> decompress

Architecture

PostgreSQL
   | (replication protocol)
WAL Receiver
   |
Local FS (fsync)
   |
Uploader (S3 / SFTP)
   |
Retention manager
   |
HTTP server (restore_command)

Continuous Backup

  • real-time WAL streaming
  • safe off-site storage
  • full PITR support
  • near-zero RPO

Kubernetes Ready

  • run as StatefulSet
  • works with StatefulSets / CNPG / Virtual Machines
  • deploy via Helm
  • GitOps-friendly

Configuration Example

main:
  listen_port: 7070
  directory: wals
receiver:
  slot: pgrwl_v5
log:
  level: trace
  format: text
  add_source: true

Testing

  • integration tests with real PostgreSQL containers
  • end-to-end WAL validation
  • unit-tested components

Why pgrwl?

  • simple deployment (single binary)
  • production-grade reliability
  • cloud-native design
  • built for Kubernetes and containers
  • secure and efficient WAL handling

Contribute

Summary

pgrwl is a lightweight, powerful, production-ready WAL archiving solution that brings:

  • streaming
  • security
  • automation
  • observability

to PostgreSQL backups.

How to Add Page Numbers to a PDF for Free — No Sign Up, No Watermark

If you’ve ever had to submit a report, share a proposal, or send a multi-page document to someone, you already know the quiet embarrassment of handing over an unnumbered PDF. Readers flip back and forth with no reference point. “Go to page 7” means nothing when there’s no page 7 visible. Page numbers aren’t decorative — they’re functional.
The problem? Most tools that let you add page numbers to a PDF online are either bloated with upsells, shove a watermark on every page, or demand you create an account before you can do anything useful.
That’s exactly the problem iHatePDF.cv was built to solve.

Why Adding Page Numbers to a PDF Is Harder Than It Should Be
Let’s be honest about the PDF ecosystem for a second. It’s dominated by a handful of giants — Adobe Acrobat at the premium end, and a pile of “free” tools in the middle that aren’t really free. You go to edit a PDF and you’re either paying $20/month or dealing with watermarks, file size limits, forced account registrations, and files being uploaded to servers you’ve never heard of.
For something as simple as adding a page number to a document, none of that should be necessary.
Searches like “free pdf editor no sign up”, “add page numbers to pdf free”, and “free online pdf editor without watermark” get tens of thousands of queries every month — and most of the results lead to tools that quietly disappoint. The free tier runs out. The watermark appears. The sign-up wall goes up.

What iHatePDF.cv’s Page Numbers Tool Actually Does
The Add Page Numbers to PDF tool on iHatePDF.cv is refreshingly straightforward. You drop in your PDF, configure how you want the numbers to look, and download. That’s it. No account. No watermark. No upload to a remote server.
Here’s what you can actually configure:

  1. Number Format
    Not everyone wants plain “1, 2, 3.” The tool gives you five formats to choose from:

1, 2, 3 — the standard numeric format
i, ii, iii — Roman numerals, great for front matter in academic documents or legal filings
A, B, C — alphabetical, useful for appendices or exhibit labelling
Page 1 — written-out format, common in formal reports
1 / 10 — shows progress (current page out of total), good for presentations and manuals

  1. Position — Six Placements
    You can place your page number in any of six spots on the page:

Top left, top center, top right
Bottom left, bottom center, bottom right

An interactive position picker in the UI makes this visual and intuitive — you click the spot on a mini page diagram, not a dropdown.

  1. Color Customization
    Seven presets (dark grey, black, red, blue, green, purple, orange) plus a full color picker if you want to match your brand or document style exactly.
  2. Font Size and Weight
    Slider control from 6pt to 48pt, plus a toggle between regular and bold. Small touches, but they matter when the numbers need to blend into a polished document or stand out on a technical one.
  3. Start Number and Skip First Page
    You can start numbering from any number — useful when your PDF is a chapter in a larger document that starts at page 34, for example. There’s also a single checkbox to skip the first page, which is the right behavior for most cover-page documents.

The Privacy Angle: Files Never Leave Your Browser
This is worth spelling out clearly because it’s non-obvious and genuinely important.
iHatePDF.cv processes everything locally in your browser using PDF-lib and PDF.js. Your file is never uploaded to any server. The page numbers are added entirely client-side, in memory, and the resulting file is downloaded directly to your device.
For anyone working with confidential documents — legal contracts, financial reports, HR materials — this isn’t a minor feature. It’s the whole point. Tools that upload your files to process them are making a choice you may not be aware of. iHatePDF.cv made the opposite choice.

Live Preview Before You Commit
One thing that separates this tool from the more barebones free options is the live preview panel. As you adjust the format, position, color, and font size, the first five pages of your PDF update in real time to show you exactly where the numbers will appear and what they’ll look like.
This catches problems before they become problems. You can see immediately if “bottom-center” looks awkward on your particular layout, or if the font size you chose is too small to read comfortably.

Who This Is Actually For
The short answer: anyone who works with PDFs regularly and doesn’t want to pay Adobe’s subscription fee for a one-off formatting task.
More specifically:
Students and researchers — Academic papers, thesis documents, and reports almost universally require page numbers. Roman numerals for the front matter, Arabic numerals for the body is a classic format that this tool handles directly by letting you set a custom start number per section.
Legal and compliance professionals — Legal documents need precise pagination. The ability to choose “Page 1” format or “1 / 10” totals, and to place numbers in a specific corner per organizational style guides, is practical here.
Business users — Proposals, decks exported as PDFs, business reports handed off to clients. Page numbers make these documents look finished and professional.
Developers and technical writers — Documentation and manuals are obvious candidates. The fact that this tool is entirely browser-based also makes it a clean fit for privacy-conscious technical environments.

How It Compares to the Usual Alternatives
ToolFree?Watermark?Upload Required?No Sign-Up?Adobe AcrobatPaidNoYesNoiLovePDFLimitedSometimesYesPartialSmallpdfLimitedSometimesYesPartialiHatePDFYesNoNoYes
The positioning is deliberate. iHatePDF.cv isn’t trying to compete on feature breadth with Acrobat. It’s competing on the things that actually matter for most common PDF tasks: free, fast, private, no friction.

Step-by-Step: Adding Page Numbers in About 30 Seconds

Go to ihatepdf.cv/page-numbers
Click “Choose PDF” or drag and drop your file
Select your number format (1,2,3 / i,ii,iii / A,B,C / Page 1 / 1/10)
Click your preferred position on the page diagram
Adjust color, font size, and weight if needed
Set your start number, toggle “skip first page” if you have a cover
Click Add Numbers
Your numbered PDF downloads instantly

The live preview updates as you go, so by the time you hit the button, you already know exactly what you’re getting.

Final Thought
The best tools get out of your way. They do the one thing they promise, they do it well, and they don’t try to upsell you at every step or hold your file hostage behind a paywall.
The iHatePDF.cv page numbers tool is exactly that. It’s free, it’s private, it works in your browser without an account, and it gives you more formatting control than most tools you’d actually pay for.
If you regularly deal with PDFs — and in 2025, who doesn’t — it’s worth bookmarking.

Try it at ihatepdf.cv/page-numbers — free, no sign up, no watermark.