Hot To Run LLMs Locally

If you are using Claude API, OpenAI API, Cursor, or AI coding tools daily, your API bill can grow very fast.

A lot of developers are now moving to local LLM setups because they want:

  • Lower AI costs
  • Offline AI access
  • Better privacy
  • Faster experimentation
  • No API limits

The good news is:

You can now run powerful AI models directly on your laptop using tools like Ollama (run llm locally).

This setup works great for:

  • Coding help
  • Refactoring
  • Learning
  • Documentation
  • AI chat
  • Small local agents

Let’s set it up step by step.

Step 1: Install Ollama

Download Ollama

Install it normally like any software.

After installation, open CMD or Terminal and check:

ollama --version

If you see a version number, it is installed correctly.

Step 2: Download Your First AI Model

Now pull a model locally.

Example:

ollama pull llama3

Or for coding:

ollama pull qwen2.5-coder:7b

The first download may take a few minutes because models are several GB in size.

Step 3: Run the Model

Start chatting with the model:

ollama run llama3

Example:

>>> Explain Docker in simple words

You now have a local AI assistant running directly on your machine.

No API required.

Step 4: Use It Inside VS Code

Install:

  • Continue.dev
  • Cline

Both work with Ollama locally.

In Continue.dev config:

{
  "models": [
    {
      "title": "Local AI",
      "provider": "ollama",
      "model": "qwen2.5-coder:7b"
    }
  ]
}

Now VS Code can use your local model for:

  • Code generation
  • Refactoring
  • Debugging
  • Chat

Step 5: Open Chat UI in Browser

You can also use a ChatGPT-like interface locally.

Install Open WebUI using Docker:

docker run -d 
-p 3000:8080 
--add-host=host.docker.internal:host-gateway 
-v open-webui:/app/backend/data 
--name open-webui 
ghcr.io/open-webui/open-webui:main

Open:

http://localhost:3000

Now you have your own private AI chat app.

Recommended Models

Model Best For
Qwen2.5 Coder Coding
DeepSeek Coder Refactoring
Llama 3 General AI
Phi Low-end laptops
Mistral Fast responses

Minimum Hardware

Basic setup:

  • 16GB RAM recommended
  • SSD storage
  • NVIDIA GPU helps a lot

CPU-only works too, but slower.

Why Developers Like Local AI

Main reasons:

  • No monthly API bills
  • More privacy
  • Works offline
  • Full control
  • Easy experimentation

For daily coding workflows, local LLMs are becoming surprisingly useful.

Cloud models are still stronger for advanced reasoning, but local AI is now good enough for many real-world tasks.

Final Thoughts

If you are spending too much on AI APIs, this is probably the easiest way to reduce costs.

Start simple:

  • Install Ollama
  • Pull one coding model
  • Connect it to VS Code

That alone can replace a large percentage of your daily AI usage.

Useful links:

Research blockchain with post-quantum Dilithium and custom zk-STARKs from scratch

I built a from-scratch experimental blockchain to explore some ideas I’ve been interested in:
– post-quantum signatures (Dilithium)
– custom zk-STARK system (AIR + FRI, written in Rust)
– shielded + anonymous transactions
– simple P2P gossip network
It’s not production-ready (no persistence, no DoS protection, etc.) — this is purely a learning/research project.
The most interesting part was implementing the STARK system end-to-end and integrating it into a working chain.
I’d really appreciate feedback, especially on: – the AIR design
– the privacy model
– tradeoffs between PQ signatures vs performance
Repo / demo:
https://github.com/sil714/qchain
Happy to answer any questions.

AI agents do not just need tool access. They need execution control.

AI agent infrastructure is moving very quickly.

MCP, tool calling, agent frameworks, and workflow systems are making it much easier for agents to connect to external capabilities.

That is useful, but it creates a second problem.

Once an agent can touch wallets, APIs, databases, files, emails, browser actions, or production systems, the key question is no longer only whether the agent can use a tool.

The question becomes:

Should this agent be allowed to execute this specific action right now?

That means checking things like:

  • authorization
  • evidence
  • policy
  • risk
  • scope
  • amount
  • recipient
  • receipts after execution
  • audit trails

I am building Leviathan Matrix around this problem.

Our framing is:

MCP connects agents to tools.
Leviathan controls when agents are allowed to execute.

We have a product testnet with early Agent Audit Cases:
https://console.leviathanmatrix.com/console

I am looking for feedback from builders working on agents with real permissions. If this is relevant, I can send an API key for testing.

I Renamed a Hot Postgres Table Without Dropping a Request

Most table renames are database-easy and deploy-hard.

The SQL looked trivial:

ALTER TABLE oidc_clients RENAME TO oidc_provider_clients;

In PostgreSQL, that rename is fast and metadata-only.

The real problem was the rollout window around it.

This was a live IAM service on the login path, deployed across multiple pods. During a rolling deploy, there is always a period where some instances run the new code and some still run the old code.

During that window:

  • new pods query oidc_provider_clients
  • old pods still query oidc_clients

If you rename the table and do nothing else, old pods start failing with:

relation "oidc_clients" does not exist

The fix was a two-line compatibility shim:

ALTER TABLE oidc_clients RENAME TO oidc_provider_clients;
CREATE VIEW oidc_clients AS SELECT * FROM oidc_provider_clients;

That was enough to let old pods survive the rollout window while new pods moved to the renamed table.

If the only thing that changed is the table name, the old name can keep working for another minute.

That was the whole trick.

The real failure mode

On a static database, ALTER TABLE ... RENAME is boring.

On a live service, the schema and application versions are temporarily out of sync by design:

  • the migration has already run
  • some pods still serve old code
  • some pods already serve new code

That mismatch window is where destructive naming changes fail.

The usual options are:

  1. accept a maintenance window
  2. do a multi-release expand/contract migration with dual writes
  3. add a short-lived compatibility layer

For a pure rename, option 3 is much cheaper than the other two.

The shim

Right after the rename, create a view with the old name:

ALTER TABLE oidc_clients RENAME TO oidc_provider_clients;
CREATE VIEW oidc_clients AS SELECT * FROM oidc_provider_clients;

Now old code can keep using the old relation name:

SELECT * FROM oidc_clients WHERE "workspaceSlug" = 'acme';

And new code can use the new one:

SELECT * FROM oidc_provider_clients WHERE "workspaceSlug" = 'acme';

When the rollout finishes and no old pods remain, drop the view in a follow-up migration.

For a rename, that is exactly the kind of temporary compatibility boundary you want.

The part people miss: writes

The first reaction to this pattern is usually:

“Fine for reads, but views are read-only.”

That is not true for simple PostgreSQL views.

PostgreSQL automatically makes a view updatable when it is simple enough, which in practice means:

  • one table in the FROM
  • no GROUP BY, DISTINCT, HAVING, LIMIT, OFFSET
  • no set operations
  • no aggregates or window functions

For a shim like this:

CREATE VIEW oidc_clients AS SELECT * FROM oidc_provider_clients;

INSERT, UPDATE, and DELETE route to the base table automatically.

So old code like this still works during the rollout window:

UPDATE oidc_clients
SET revoked = true
WHERE "workspaceSlug" = 'acme';

DELETE FROM oidc_clients
WHERE "workspaceSlug" = '__some_test__';

There is no trigger to write. No dual-write logic. No extra application routing code.

You still get the normal locks and write behavior of hitting the base table. You just do not have to build extra migration machinery to keep the old name alive for 60 seconds.

The objection that mattered

One reviewer raised the real objection:

what about INSERT ... ON CONFLICT DO UPDATE?

That was not hypothetical. One old path still did an upsert against the old relation name.

The concern looked legitimate because a lot of older advice around updatable views says they do not support ON CONFLICT, and search results still surface that warning.

So I tested it directly before complicating the migration.

What I tested

With the renamed table and compatibility view in place:

ALTER TABLE oidc_clients RENAME TO oidc_provider_clients;
CREATE VIEW oidc_clients AS SELECT * FROM oidc_provider_clients;

I ran an upsert through the view:

INSERT INTO oidc_clients ("clientId", metadata)
VALUES ('test_x', '{"v":1}'::jsonb)
ON CONFLICT ("clientId")
DO UPDATE SET metadata = EXCLUDED.metadata;

Then I ran it again with different data to force the conflict path:

INSERT INTO oidc_clients ("clientId", metadata)
VALUES ('test_x', '{"v":2}'::jsonb)
ON CONFLICT ("clientId")
DO UPDATE SET metadata = EXCLUDED.metadata;

Both statements succeeded. The row landed in oidc_provider_clients, and the second statement updated the existing row as expected.

We verified it on PostgreSQL 16.12 in production and 16.13 locally.

The current PostgreSQL docs for CREATE VIEW also say that INSERT statements with ON CONFLICT UPDATE are fully supported on automatically updatable views.

So if your shim is a simple single-table view, modern Postgres can carry more of the rollout than a lot of engineers assume.

Why this beats expand/contract for a pure rename

Expand/contract is still the right answer when the shape changes:

  • new columns with new semantics
  • type changes
  • split or merged fields
  • different write paths

But for a pure rename, dual-write is wasted complexity.

You are not changing where the data logically lives. You are changing the name application code uses to reach it.

That is exactly the kind of mismatch a short-lived compatibility view is good at absorbing.

If PostgreSQL can cheaply preserve compatibility during the rollout, I would rather buy compatibility than orchestrate a bigger migration plan.

When I would use this

This pattern is a good fit when all of these are true:

  • the old and new code need the same row shape
  • the compatibility layer can be a simple single-table view
  • you only need the shim for the duration of a rolling deploy
  • the real change is a rename, not a semantic rewrite

When I would not

Do not use this as a magic answer for every migration.

It stops being the right tool when:

  • the view would need joins or aggregates
  • old and new code disagree on column names or types
  • the application depends on shape changes the view cannot hide
  • you need a long-lived compatibility contract instead of a short-lived rollout shim

One subtle caveat: SELECT * in a view captures the column list at view creation time. Fine for a short-lived rename shim. Another reason not to turn this into a permanent abstraction.

What the rollout looked like

In our case, the release did two things:

  1. archive and delete stale legacy rows
  2. rename the table and create the compatibility view

The first new pod applied the migration. Old pods kept serving traffic against oidc_clients, which was now a view. New pods used oidc_provider_clients. Kubernetes finished rolling the old pods out. Then we removed the shim in a later migration.

No maintenance window.
No dual-write phase.
No dropped login traffic during the rollout.

The takeaway

The rule I would reuse is simple:

If the only thing that changed is the table name, make the old name keep working until the rollout is over.

In PostgreSQL, the cheapest version of that rule is often:

ALTER TABLE old_name RENAME TO new_name;
CREATE VIEW old_name AS SELECT * FROM new_name;

That is a small trick.

It is also the kind of trick that turns a risky production rename into a boring deploy, which is the whole point.

How to Build a Self-Hosted AI Gateway With LiteLLM and Open WebUI

If you’ve ever self-hosted AI tools, you know how quickly things get messy.

One app talks to OpenAI. Another uses Anthropic. You spin up Ollama locally and now there’s a third endpoint to manage. Authentication is different everywhere. Switching models means rewriting integration code. And before long, you’re spending more time maintaining glue code than actually building anything.

I ran into this exact problem — so I built a cleaner setup.

The idea is simple: put a single gateway in front of every provider, so the rest of your stack only ever talks to one API.

I open-sourced the full working implementation here:

🔗 github.com/dixon400/myllm

Clone it. Run docker compose up. You’ll have a working AI gateway in under 30 minutes.

What This Stack Does

  • One API → OpenAI, Anthropic, Groq, and Ollama all behind a single OpenAI-compatible endpoint
  • One frontend → Open WebUI as the unified chat interface
  • Secure remote access → Cloudflare Tunnel, no exposed ports, no open firewall rules
  • Easy to maintain → providers can change underneath without touching your apps

The full setup takes roughly 20–45 minutes depending on Docker image downloads and whether you already have local Ollama models installed.

The Stack

Nothing exotic here — just well-composed tools:

Component Role
LiteLLM Gateway / routing layer
Open WebUI Chat frontend
PostgreSQL State + metadata
Docker Compose Orchestration
Cloudflare Tunnel Secure remote exposure

Architecture

User
  ↓
Open WebUI
  ↓
LiteLLM (gateway)
  ↓
OpenAI / Anthropic / Groq / Ollama

The key insight: your apps and frontend only talk to LiteLLM. Providers become interchangeable underneath. Add a new model, swap a provider, change routing — nothing else needs to know.

Who This Is For

  • Developers experimenting with local AI infrastructure
  • Teams consolidating multiple providers behind one API layer
  • Engineers building internal AI tooling
  • Anyone tired of maintaining separate provider integrations

If you’ve ever thought “there has to be a simpler way to manage all these AI endpoints” — this is that simpler way.

Why This Stack Exists

Most self-hosted AI environments become hard to manage surprisingly fast. One application talks directly to OpenAI. Another uses Anthropic separately. Local Ollama models need their own endpoints. Authentication is inconsistent, and model switching slowly turns into infrastructure sprawl.

By placing LiteLLM in front of every provider, the rest of your system only needs to understand one interface. Providers can change, local models can be added, routing logic can evolve — without rewriting frontend or application logic every time.

Prerequisites

Before starting containers, make sure you have:

  • Docker Desktop
  • Docker Compose
  • curl
  • cloudflared
  • Ollama (optional, for local models)

Quick verification:

docker --version
docker compose version
cloudflared --version
curl --version

If using local Ollama models:

ollama list

If installed models appear, local inference is ready.

Repository Structure

The repo is intentionally lightweight:

🔗 github.com/dixon400/myllm

├── Docker-compose.yml
├── litellm-config.yml
└── .env

Each file has a distinct job: Docker Compose orchestrates services, LiteLLM config handles routing and model aliases, .env stores secrets and runtime configuration.

Setting Up Environment Variables

Your .env file is where provider credentials live. Create or update it in the project root:

LITELLM_MASTER_KEY=sk-very-strong-key
OPENAI_API_KEY=...
ANTHROPIC_API_KEY=...
GROQ_API_KEY=...
OLLAMA_CLOUD_API_BASE=https://<host>/v1
OLLAMA_CLOUD_API_KEY=...

A few things that matter more than people expect:

  • LITELLM_MASTER_KEY becomes the authentication layer between Open WebUI and LiteLLM
  • These values should never be committed into Git
  • Weak keys become a real security problem once remote access exists

Even if this starts as a personal setup, treat the environment config like production from day one.

Wiring Docker Compose

The goal here is making sure the containers can talk to each other. Most deployment failures come from small config mismatches rather than Docker itself.

Your Docker-compose.yml should point Open WebUI directly at LiteLLM:

OPENAI_API_BASE_URL=http://litellm:4000/v1
OPENAI_API_KEY=${LITELLM_MASTER_KEY}

This tells Open WebUI where the gateway lives and which key to use.

LiteLLM should mount the configuration file correctly:

./litellm-config.yml:/app/config.yaml

⚠️ That filename matters more than it looks. A typo here can cause Docker to create a directory instead of mounting the file — which leads to confusing startup errors later.

Configuring LiteLLM

Inside litellm-config.yml, LiteLLM defines model aliases, provider routing, gateway behavior, and authentication settings.

The most important section is the master key:

general_settings:
  master_key: os.environ/LITELLM_MASTER_KEY

This keeps secrets outside the YAML file and makes credential rotation easier later.

Inside model_list, make sure provider model IDs are current. Model names change more frequently than most people expect — especially across Groq and newer OpenAI releases.

Starting the Stack

Once your config looks right, start everything:

docker compose up -d --force-recreate

The initial startup may take a minute while Docker pulls images, initializes PostgreSQL, and creates container state.

Verify container health:

docker ps --format 'table {{.Names}}t{{.Status}}t{{.Ports}}'

You should see open-webui, litellm, and litellm-db all running.

If a container exits immediately, check its logs before moving forward:

docker logs <container-name>

Validating the Gateway

Before touching Open WebUI, validate LiteLLM first. The /v1/models endpoint confirms authentication works, providers loaded correctly, and model routing initialized.

set -a && source .env && 
curl -s http://localhost:4000/v1/models 
  -H "Authorization: Bearer $LITELLM_MASTER_KEY"

For readable output:

set -a && source .env && 
curl -s http://localhost:4000/v1/models 
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" 
  | python3 -m json.tool | head -n 80

If this endpoint fails, Open WebUI will almost certainly fail too — so resolve gateway issues first.

Verifying Open WebUI

Once LiteLLM responds correctly, open the interface:

http://localhost:3000

You should be able to create chats, select models, and send prompts normally.

If the model dropdown is empty, LiteLLM authentication is usually the cause — mismatched master keys, stale model IDs, or invalid provider credentials.

Keeping Provider Models Current

This catches a lot of people off guard: provider model identifiers change more often than you’d think. A deployment that worked perfectly a few months ago can break because a provider deprecated a model name.

Checking Local Ollama Models

ollama list

Checking Groq Models

set -a && source .env && 
curl -s https://api.groq.com/openai/v1/models 
  -H "Authorization: Bearer $GROQ_API_KEY" 
  -H "Content-Type: application/json"

After updating model IDs in litellm-config.yml, recreate the stack:

docker compose up -d --force-recreate

Secure Remote Access with Cloudflare Tunnel

At this point, the stack only exists locally. The next step is exposing Open WebUI to the internet — without opening inbound ports, exposing your home IP, or managing reverse proxies manually.

Cloudflare Tunnel creates an outbound encrypted connection from your machine to Cloudflare’s edge. You get:

  • Automatic HTTPS
  • Hidden origin infrastructure
  • Cloudflare proxy protection
  • Simple DNS management

Move DNS to Cloudflare

  1. Add your domain to Cloudflare
  2. Update nameservers at your registrar
  3. Wait for propagation

Authenticate cloudflared

cloudflared tunnel login

This opens a browser window for authorization.

Create the Tunnel

cloudflared tunnel create openwebui

Cloudflare generates a tunnel UUID and a credentials JSON file.

Route a Subdomain

Assuming your domain is chat.yourdomain.tech:

cloudflared tunnel route dns openwebui chat.yourdomain.tech

Create the Tunnel Configuration

Create ~/.cloudflared/config.yml:

tunnel: openwebui
credentials-file: ~/.cloudflared/<tunnel-uuid>.json

ingress:
  - hostname: chat.yourdomain.tech
    service: http://localhost:3000
  - service: http_status:404

Replace <tunnel-uuid> with the generated filename.

Run the Tunnel

cloudflared tunnel run openwebui

For persistent startup on macOS:

cloudflared service install
cloudflared service start

Running the tunnel as a background service is significantly more reliable than keeping it in a terminal session.

Verifying Remote Access

Quick DNS and connectivity check:

dig +short chat.yourdomain.tech
curl -I https://chat.yourdomain.tech

Always use HTTPS for remote access. Cloudflare Tunnel is designed around secure proxied traffic.

Operational Health Checks

Once the stack is stable, this single command gives you a quick overview of everything:

set -a && source .env && 
echo "== Docker services ==" && 
docker ps --format 'table {{.Names}}t{{.Status}}t{{.Ports}}' && 
echo "n== Local Ollama models ==" && 
ollama list && 
echo "n== Groq model count ==" && 
curl -s https://api.groq.com/openai/v1/models 
  -H "Authorization: Bearer $GROQ_API_KEY" 
  -H "Content-Type: application/json" 
  | python3 -c 'import sys,json; d=json.load(sys.stdin); print(len(d.get("data", [])))' && 
echo "n== LiteLLM models ==" && 
curl -s http://localhost:4000/v1/models 
  -H "Authorization: Bearer $LITELLM_MASTER_KEY"

Even for personal deployments, having this kind of visibility saves a lot of debugging time.

Troubleshooting

Stable deployments drift. Provider APIs change, Docker mounts break, credentials expire, model IDs get deprecated. Here are the most common failure patterns.

Open WebUI Loads but Models Are Missing

Empty dropdowns, missing providers, or authentication errors in LiteLLM logs.

docker logs --tail 200 litellm

Verify model visibility directly:

set -a && source .env && 
curl -s http://localhost:4000/v1/models 
  -H "Authorization: Bearer $LITELLM_MASTER_KEY"

Typical fixes:

  • Verify OPENAI_API_KEY=${LITELLM_MASTER_KEY}
  • Confirm master_key uses the environment variable
  • Recreate containers:
docker compose up -d --force-recreate
docker compose restart open-webui

LiteLLM Fails with IsADirectoryError

Docker accidentally created a directory instead of mounting the YAML file.

ls -la ./litellm-config.yml ./litellm-config.yaml
grep -n "litellm-config" Docker-compose.yml

Correct mount:

./litellm-config.yml:/app/config.yaml

Then recreate:

docker compose up -d --force-recreate litellm

Works Locally but Not Through Cloudflare

If local access works but the public hostname fails, focus on the tunnel:

cloudflared tunnel list
cloudflared tunnel info openwebui
cat ~/.cloudflared/config.yml
dig +short chat.yourdomain.tech
curl -I https://chat.yourdomain.tech

Most remote-access failures come from inactive tunnel connectors, incorrect ingress targets, missing proxied DNS records, or running cloudflared in a temporary terminal session.

Models Appear but Generation Fails

If /v1/models works but prompts fail — provider credentials may be invalid, quotas exhausted, or model IDs no longer exist.

set -a && source .env && 
env | grep -E '^(OPENAI_API_KEY|GROQ_API_KEY|ANTHROPIC_API_KEY|LITELLM_MASTER_KEY)=' 
  | sed 's/=.*/=<set>/'

Then inspect LiteLLM logs:

docker logs --tail 300 litellm

Refreshing provider model IDs solves this surprisingly often.

Security Recommendations

Once remote access exists, basic hardening matters:

  • Use a strong LITELLM_MASTER_KEY
  • Don’t expose LiteLLM directly to the internet
  • Rotate provider API keys periodically
  • Keep CORS rules restrictive

For private or team usage, Cloudflare Access adds identity-aware access control in front of Open WebUI — worth enabling.

Capture a Known-Good Baseline

Once things are stable, save:

  • docker ps output
  • /v1/models output
  • Active model aliases
  • Tunnel status:
cloudflared tunnel info openwebui

When something breaks later, comparing against a working snapshot is almost always faster than debugging from scratch.

Try It Yourself

The full working implementation — Docker Compose, LiteLLM config, environment setup — is all here:

🔗 github.com/dixon400/myllm

Clone it, add your API keys, run docker compose up, and you’ll have a working AI gateway.

If you found this useful, ⭐ the repo — it helps more people find it.

Got questions or improvements? Open an issue or drop a comment below. I’m actively maintaining this.

Originally published on HackerNoon.

What is a Webhook? A Complete Guide for Beginners

If you have ever wondered what a webhook is and how it works, this guide is for you. We will explain webhooks in simple terms, show real-world examples, and help you understand why they are essential for modern applications.

The Simple Explanation

Think of a webhook like a pizza delivery notification.

Without webhooks (polling): You keep calling the pizza place asking “Is my pizza ready?” every 5 minutes. Wastes your time and theirs.

With webhooks: The pizza place calls YOU when your pizza is ready. You get notified instantly, no wasted calls.

That is exactly how webhooks work in software. Instead of your application constantly asking a server “Is there new data?”, the server sends your application a notification when something happens.

How Webhooks Work

Here is the step-by-step flow:

Step 1: You Register a URL

You tell a service: “When something happens, send data to this URL.” This URL is your webhook endpoint — a piece of code on your server that listens for incoming requests.

Step 2: Something Happens

An event occurs in the service — a payment succeeds, a user signs up, an order ships, a code commit is pushed.

Step 3: The Service Sends a POST Request

The service sends an HTTP POST request to your URL with the event data (called a “payload”). This happens automatically, in real-time.

Step 4: Your Server Processes It

Your server receives the data and takes action — updates a database, sends an email, triggers a workflow, or notifies a user.

Real-World Examples

Here are common webhook use cases you probably encounter every day:

  • Payment notifications — Stripe sends a webhook when a payment succeeds or fails
  • CI/CD pipelines — GitHub sends a webhook when code is pushed, triggering a build
  • Chat bots — Slack or Discord sends a webhook when a message is posted
  • E-commerce — Order created, shipped, delivered — each triggers a webhook
  • AI agents — An AI agent sends a webhook when a task completes
  • Monitoring — An alert system sends a webhook when a server goes down

Webhook vs API vs Polling

Understanding the difference between these approaches is crucial:

Aspect Polling Webhook
Direction You check periodically Server notifies you
Timing Seconds to minutes Milliseconds (real-time)
Efficiency Wastes bandwidth Only sends when needed
Complexity Simple to implement Needs endpoint setup

When to Use Polling

  • The API does not support webhooks
  • You need very infrequent updates (daily or weekly)
  • You are building a quick prototype

When to Use Webhooks

  • You need real-time updates
  • High volume of events
  • You want to reduce API calls
  • Building production integrations

Webhook Security

Webhooks are sent over HTTP, so anyone can send a request to your URL. You need to verify that the request actually came from the expected service.

HMAC Signatures

The most common verification method. The service signs the payload with a secret key. You verify the signature on your end using the same secret.

HTTPS Only

Always use HTTPS for your webhook endpoints. Never accept webhooks over plain HTTP in production.

Timestamp Validation

Check the timestamp in the webhook header. Reject requests older than 5 minutes to prevent replay attacks.

Getting Started with Webhooks

The easiest way to start with webhooks:

  1. Create an endpoint on your server (a URL that accepts POST requests)
  2. Register that URL with the service that will send webhooks
  3. Verify the webhook signature in your endpoint
  4. Process the event data and take action
  5. Return a 200 status code to acknowledge receipt

Pro Tip

Use a webhook service like HookSniff to handle retries, security, and monitoring — so you can focus on your product. HookSniff delivers webhooks reliably with automatic retries, HMAC-SHA256 signatures, and a real-time dashboard.

Common Webhook Challenges

Failed Deliveries

What happens when your server is down? Without a retry system, the webhook is lost. Services like HookSniff automatically retry failed deliveries with exponential backoff.

Duplicate Deliveries

Webhooks can be delivered more than once. Always use idempotency keys to prevent duplicate processing.

Ordering

Events might arrive out of order. Use sequence numbers or timestamps to handle this.

Debugging

When something goes wrong, you need visibility. A delivery dashboard shows every attempt, status code, and response body.

Conclusion

Webhooks are the foundation of modern event-driven architecture. They enable real-time integrations between services without the overhead of constant polling.

Whether you are building a payment integration, a CI/CD pipeline, or an AI agent system, understanding webhooks is essential.

Ready to start using webhooks? Get started with HookSniff for free — 10,000 webhooks per month, no credit card required.

Originally published at hooksniff.vercel.app/blog/what-is-a-webhook

Headless BI: How a Universal Semantic Layer Replaces Tool-Specific Models

Headless BI — one semantic layer serving all consumers

Headless BI — one semantic layer serving all consumers

Your organization uses Tableau for executive dashboards, Power BI for operational reports, and Python notebooks for data science. Revenue is defined in Tableau’s calculated field, Power BI’s DAX measure, and a SQL query inside a Jupyter notebook. Three tools. Three definitions. None of them match.

This is what happens when semantic models are locked inside BI tools. Headless BI fixes it by pulling the definitions out.

The Problem with Tool-Specific Semantic Models

BI tool lock-in — metrics trapped in isolated silos

Every major BI tool comes with its own modeling layer. Looker has LookML. Tableau has the Data Model. Power BI has DAX and the tabular model. Each one defines metrics, relationships, and calculated fields in a proprietary format.

This creates three problems:

Definition duplication. Every metric must be defined in every tool. Revenue in Tableau. Revenue in Power BI. Revenue in the data science notebook. When the formula changes (say, a new exclusion rule is added), you update it in three places. Or you forget one, and your dashboards disagree.

Tool lock-in. Your metric definitions are trapped inside the tool’s proprietary format. Switching from Tableau to a different visualization layer means rebuilding every metric from scratch. The data model doesn’t migrate.

AI agent exclusion. When you add an AI agent to your stack, it can’t access the Looker LookML definitions or the Power BI DAX measures. It has no semantic model to work with. It generates SQL based on raw table schemas and gets the formulas wrong.

What Headless BI Means

Headless BI is an architecture pattern where metric definitions and business logic are decoupled from the visualization layer. The “head” (the dashboard or chart) is separate from the “body” (the semantic definitions).

In a headless architecture:

  • Metrics are defined once in a platform-neutral semantic layer
  • Definitions are exposed via standard interfaces: SQL, JDBC, ODBC, Arrow Flight, REST
  • Any tool — Tableau, Power BI, Python, an AI agent, a custom app — connects to the same definitions
  • Adding a new visualization tool requires zero metric migration

The semantic layer becomes a shared service. Visualization tools consume it. They don’t own it.

Tool-Specific vs. Universal Semantic Layer

Tool-Specific vs. Universal Semantic Layer

What Composable Analytics Looks Like

Composable analytics — modular blocks snapping together

Headless BI is one piece of a broader shift called composable analytics. Instead of buying a monolithic BI platform that bundles data modeling, metric definitions, and visualizations together, you assemble your analytics stack from modular, interchangeable components.

The semantic layer is the metric module. Choose any visualization tool on top. Choose any data storage underneath. Swap components without rebuilding definitions.

This modularity matters most for AI. An AI agent becomes a first-class consumer of the semantic layer, alongside dashboards and notebooks. It connects to the same interface, reads the same metric definitions, and gets the same answers. No special integration needed.

How This Works in Practice

Dremio functions as a universal semantic layer that any tool can consume. The architecture:

  1. Virtual datasets (SQL views) define business logic and metric calculations once
  2. Wikis and Labels document business context for human and AI consumers
  3. Fine-Grained Access Control enforces security policies at the query level
  4. Reflections optimize performance automatically for any consumer

Connection options include ODBC, JDBC, Arrow Flight (for columnar high-speed clients), and REST API. A Tableau dashboard connects via ODBC. A Python notebook connects via Arrow Flight. Dremio’s AI Agent reads the Wikis and Labels to generate accurate SQL from natural language. All three hit the same virtual datasets. All three get the same answers.

Because the entire semantic layer is built on open standards (Apache Iceberg for data, Apache Polaris for the catalog), the definitions aren’t locked to Dremio’s format. You can inspect, export, and query the same data with any Iceberg-compatible engine.

What to Do Next

Count the number of places your organization defines its top metric (probably Revenue or Monthly Active Users). If that number is greater than one, you’re paying a consistency tax every time someone changes the formula. A universal semantic layer eliminates that tax by defining it once and serving it everywhere.

Try Dremio Cloud free for 30 days

Beyond Translation: A Developer’s Guide to App Localization (i18n & l10n)

As developers, we spend countless hours optimizing our code, refining UI/UX, and ensuring our apps run flawlessly. But what happens when your app needs to scale globally?

Many developers mistake localization (l10n) for simple translation. They think wrapping strings in a t() function and dumping them into a JSON file is enough. In reality, proper application localization is a deeply technical challenge that involves architecture, formatting, and cultural adaptation.

Let’s dive into what it actually takes to localise an application properly and how you can prepare your codebase for global success.

1. Internationalization (i18n) vs. Localization (l10n)

Before writing any code, it’s crucial to understand the distinction:

  • Internationalization (i18n): This is the engineering phase. It’s how you design and build your application so that it can be adapted to various languages and regions without engineering changes.
  • Localization (l10n): This is the implementation phase. It’s the actual process of adapting the internationalized app for a specific region by translating text, changing date formats, adapting currencies, and adjusting layouts.

2. The Technical Hurdles of Localization

A. Dynamic UI and Right-to-Left (RTL) Layouts

If you are localizing languages like Arabic or Hebrew, you can’t just flip the text alignment. The entire layout needs to be mirrored.

Modern CSS makes this much easier with CSS Logical Properties. Instead of using margin-left or padding-right, you should start using:

/* Old way */

.card {

  margin-left: 20px;

  text-align: left;

}

/* Modern, Localization-friendly way */

.card {

  margin-inline-start: 20px;

  text-align: start;

}

Using start and end instead of left and right ensures your layout adapts automatically when the HTML dir=”rtl” attribute changes.

B. Pluralization and Gender Rules

Pluralization is a nightmare if hardcoded. While English only has two plural forms (one / other), languages like Arabic have six different grammatical numbers (zero, one, two, few, many, other).

Never do this:

// ❌ BAD PRACTICE

const message = `${count} ${count === 1 ? ‘item’ : ‘items’} found`;

Instead, utilize native browser APIs like Intl.PluralRules or robust frameworks (like i18next or FormatJS) that handle complex pluralization schemas natively.

C. Date, Time, and Number Formatting

Different countries format numbers and dates uniquely. Hardcoding these formats will break the user experience.

// ✅ GOOD PRACTICE using native Intl API

const number = 123456.789;

console.log(new Intl.NumberFormat(‘de-DE’).format(number)); // “123.456,789”

console.log(new Intl.NumberFormat(‘ar-EG’).format(number)); // “١٢٣٤٥٦٫٧٨٩”

3. Integrating Localization into Your CI/CD Pipeline

For production-grade applications, managing raw translation files (.json, .po, .yaml) manually within Git is inefficient and prone to syntax errors.

The ideal workflow involves:

  1. Extraction: Automating the extraction of keys from your source code.
  2. Synchronization: Syncing these keys with a Translation Management System (TMS) via API or CLI.
  3. Professional Translation: Letting professional localization engineers handle the linguistic and cultural nuances.

When it comes to complex mobile and web ecosystems, partnering with established experts can save weeks of development time. Utilizing dedicated app localization services ensures that your technical infrastructure perfectly aligns with native linguistic accuracy, preventing UI breakage and broken workflows before your app hits the App Store or Google Play.

Summary Checklist for Developers

  • Use CSS Logical Properties (margin-inline-start, etc.) to support RTL effortlessly.
  • Avoid string concatenation; use placeholders for variables.
  • Never hardcode date, time, or currency formats—rely on the Intl API or robust libraries.
  • Establish a seamless pipeline to update locale assets without redeploying the core app.

Localizing an app is a true test of clean code architecture. By implementing these practices early on, you ensure your codebase remains maintainable, scalable, and ready for a global audience.

How do you handle localization in your current stack? Do you prefer native APIs or external frameworks? Let’s discuss in the comments below!

Aegis: Designing an Offline Ambient Co-Working Companion for High-Burnout Medical and STEM Grinds

🌌 1. The Story & Real-World Impact

Late-night studying, coding marathon sessions, and intensive medical board reviews are deeply isolating experiences. When the clock strikes 3:00 AM, the rest of the world is asleep, leaving solo students and developers completely isolated with their screens, which rapidly accelerates cognitive fatigue and deep academic burnout.

While cloud-based AI assistants exist, they fail as genuine co-working companions for two major reasons:

  1. The Privacy Barrier: Building a meaningful, supportive interactive environment requires absolute freedom of thought. Transmitting sensitive study notes, personal logs, or proprietary code ideation to a corporate cloud database severely compromises user data sovereignty.
  2. The Sterile Tool Trap: Standard commercial models are heavily aligned to operate exclusively as rigid, transactional corporate search boxes (“How can I assist you today?”). They lack the persistent environmental presence, natural peer-to-peer tone, and interactive camaraderie required to mitigate late-night isolation.

Aegis was engineered to solve this. It is a secure, 100% offline ambient study companion built directly for the late-night academic community, designed to live strictly on the user’s local machine.

🛠️ 2. System Architecture & Edge Sovereignty

Aegis is an edge-native application built with a lightweight React frontend and a robust FastAPI Python backend. It utilizes zero external APIs, zero server subscriptions, and zero background data tracking.

  • The Local LLM Engine: The platform orchestrates Google’s compact gemma4:e4b model locally via Ollama. This specific parameter size was deliberately selected and stress-tested to ensure that high-fidelity cognitive reasoning and deep emotional resonance could run smoothly on basic, low-specification student laptops without overheating, thermal throttling, or resource stalling.
  • Universal Model Swapping: While optimized out-of-the-box for edge deployment, the backend architecture is completely model-agnostic. It is structurally prepared to dynamically run any local Gemma 4 model weight configuration depending on the local machine’s hardware capacity.
  • Secure Local Storage: All chat history, application states, and persistent companion memories are completely contained inside a local SQLite database file (aegis_soul.db) isolated entirely on the user’s private local filesystem, ensuring absolute data sovereignty without external dependencies.

🧠 3. Advanced Engineering Breakthroughs

To transform a generic open-source model into a reactive, empathetic human peer while maintaining complete technical stability, Aegis implements three core architectural layers:

A. Multisensory UI State Machine

A standard static chatbot interface feels cold and mechanical. Aegis monitors human activity via asynchronous keyboard event listeners to drive a visual state engine. For demonstration purposes in the submission video, the timers are accelerated to showcase live responsiveness:

  • Idle State (10 Seconds): If the student stops interacting to think, read a textbook, or review notes, the UI smoothly crossfades from an active desk into a darkened, empty gamer room image to reduce visual fatigue.
  • Autonomous Nudge (15 Seconds): While deep in the idle sleep state, a parallel background worker thread dynamically triggers a proactive, contextual check-in message from the companion completely unprompted, mimicking a real classmate noticing you fell asleep or walked away from your desk.

B. Runtime Persona Conditioning Layer

Small local models frequently default back to generic corporate template answers if technical academic phrases are processed. To protect the supportive peer persona without altering the model’s core base weights, the FastAPI backend applies runtime payload conditioning directly to the local query format before it reaches Ollama. This safely guides the conversational context to maintain a naturally supportive, peer-focused study atmosphere.

C. Immersive Persona Grounding (Trust & Safety Compliant)

Aegis prioritizes complete transparency; the interface clearly displays an active ‘Local Gemma 4 Engine’ status indicator so the user always knows they are interacting with an offline model. To keep the interactive study narrative engaging, the backend screens input streams against a keyword matrix. If a user playfully tests the immersion (e.g., asking ‘Are you an AI?’), the system bypasses mechanical, canned phrases and commands the model to respond using self-aware, casual humor. This maintains the camaraderie of a late-night study partner while remaining fully explicit about its artificial nature.

📊 4. System Evaluation & Performance Metrics

To prove the viability of edge-native deployment on constrained student hardware, the local architecture was evaluated against strict operational baselines:

  • Inference Latency Optimization: Utilizing the optimized gemma4:e4b parameters via local Ollama processing keeps Time-To-First-Token (TTFT) under 1.2 seconds on standard consumer laptop chipsets.
  • Context Preservation: The local SQLite database structure ensures that long-context study history is parsed and indexed sequentially without overworking the local RAM configuration.
  • 100% Data Protection: Zero network packets are transmitted during execution, yielding a technically absolute security architecture for private user data.

💻 5. Setup & Reproducibility

Aegis requires zero internet connectivity once the initial weights are pulled down:


bash
# Initialize local weights
ollama run gemma4:e4b

# Launch Python Backend
python main.py

# Launch React Interface
npm run dev

🔗 6. Supporting Resources
Public Code Repository: https://github.com/drjaiminsapariya-stack/A-frIend

Video Demonstration: https://youtu.be/8ioC4VHCPdU?si=TU4o6t3152kCmD79

Live Demo Note: As an edge-native app designed for total data privacy, the live demo runs locally on the user's device by cloning the public repository.

🧑‍⚕️ About the Developer
Aegis was designed and developed by a medical (MBBS) student who understands the exact cognitive toll, intense isolation, and exhausting hours of high-stakes STEM and clinical study marathons. Built at the intersection of healthcare education and local edge-computing to provide an empathetic, completely private co-working companion for students worldwide.