Prisma Generator NestJS DTO — pluggable DTOs with annotations and custom generators

If you build NestJS APIs on top of Prisma, you’ve probably felt the friction: your schema is the source of truth, but your DTOs, validation rules, and Swagger metadata live somewhere else — and they drift.

Most Prisma DTO generators solve the basics (Create / Update / Entity classes with decorators). That’s useful, but the moment you need custom output — audit metadata, GraphQL types, RBAC manifests, your own validators — you’re back to hand-written glue code.

@tommasomeli/prisma-generator-nestjs-dto is a Prisma generator built for that second phase. It emits the usual NestJS DTOs, then gets out of your way — with a plugin API, annotation-driven decorators, and a type-safe config file when the Prisma schema block isn’t enough.

What you get out of the box

For every Prisma model User, run npx prisma generate and you get:

src/generated/nestjs-dto/
  user/
    user.entity.ts
    create-user.dto.ts
    update-user.dto.ts
    index.ts
  index.ts

Each file ships with:

  • class-validator decorators (@IsString, @IsOptional, …)
  • @nestjs/swagger metadata (@ApiProperty, @ApiHideProperty, …)
  • Self-contained imports — relation DTOs, @DtoOverrideType targets, and annotation arguments are resolved automatically

Configure it directly in schema.prisma:

generator nestjsDto {
  provider           = "prisma-generator-nestjs-dto"
  output             = "../src/generated/nestjs-dto"
  outputType         = "class"
  outputStructure    = "nestjs"
  fileNamingStrategy = "kebab"
  reExport           = "true"
  classValidator     = "true"
  swaggerDocs        = "true"
  prettier           = "true"
}

Install:

npm i -D @tommasomeli/prisma-generator-nestjs-dto

Annotations — control visibility without touching generated code

Triple-slash comments (///) above models and fields drive the built-in generators. No post-processing, no manual edits.

model User {
  id           Int    @id @default(autoincrement())
  email        String @unique
  name         String
  /// @DtoHidden
  passwordHash String
  /// @DtoReadOnly
  createdAt    DateTime @default(now())
}
Annotation Effect
@DtoHidden Hide everywhere
@DtoReadOnly Exclude from Create and Update DTOs
@DtoEntityHidden Hide in the Entity (API response)
@DtoCreateHidden / @DtoUpdateHidden Hide in one DTO only
@DtoOverrideType(MyType) Override the TypeScript type (auto-imported)
@DtoIgnoreModel Skip the model entirely

You can also bind your own validators and decorators via config — more on that below.

The plugin system — when built-in DTOs aren’t enough

The differentiator is extraGenerators: drop in any class extending BaseGenerator and it runs alongside the built-ins in the same pipeline.

A plugin receives:

  • The parsed model graph (with annotations on every field)
  • Resolved config (extraDecorators, extraValidators, extraImports, …)
  • Import-merging helpers (addImport, formatImports, getTemplate)

Here’s a minimal plugin that reuses the built-in renderer to emit an Entity without class-validator:

import { isEntityHidden } from '@tommasomeli/prisma-generator-nestjs-dto';
import { BaseGenerator } from '@tommasomeli/prisma-generator-nestjs-dto';
import type { Field, File, Model } from '@tommasomeli/prisma-generator-nestjs-dto';

export default class EntityDtoGenerator extends BaseGenerator {
  filePrefix = '';
  fileSuffix = '.entity';
  classPrefix = '';
  classSuffix = '';

  async generate(): Promise<File[]> {
    return this.models.map((model) => {
      const filteredFields = model.fields.filter((f: Field) => !isEntityHidden(f));
      const processedModel: Model = { ...model, fields: filteredFields as Field[] };
      const outputPath = this.getPath(model);
      return {
        path: outputPath,
        content: this.getTemplate({ model: processedModel, classValidator: false, outputPath }),
      };
    });
  }
}

Lifecycle hooks

Plugins can hook the full run:

  • beforeAll(models) — mutate the shared model list before any generator runs (shared pre-pass)
  • afterAll(files) — append barrels, audit reports, or aggregated indexes after all generators finish

TypeScript plugins, no precompilation

Point extraGenerators at a .ts file and the generator loads it via jiti. No build step required for your plugin.

Real example: custom @Auditable annotation

The repo includes a runnable example under examples/blog/.

Schema — annotate models with a custom @Auditable name:

/// @Auditable("user_audit")
model User {
  id           Int      @id @default(autoincrement())
  email        String   @unique
  /// @DtoHidden
  passwordHash String
  posts        Post[]
}

/// @Auditable("post_audit")
model Post {
  id       Int    @id @default(autoincrement())
  title    String
  author   User   @relation(fields: [authorId], references: [id])
  authorId Int
}

Config — register the annotation and the plugin:

import { from, type GeneratorConfigFile } from '@tommasomeli/prisma-generator-nestjs-dto';

export default {
  extraAnnotations: ['Auditable'],
  extraGenerators: from('./generators/audit-generator.ts', ['AuditGenerator']),
} satisfies GeneratorConfigFile;

Output — alongside the usual DTOs, you get per-model audit metadata and an aggregated index:

generated/
  user/user.audit.ts
  post/post.audit.ts
  audit-index.ts    ← emitted by AuditGenerator#afterAll

Type-safe external config

Prisma’s generator block is great for simple flags, but it can’t express nested objects or multi-line arrays. For anything richer, use a configFile:

generator nestjsDto {
  provider   = "prisma-generator-nestjs-dto"
  output     = "../generated"
  configFile = "../nestjs-dto.config.ts"
}

The from() helper validates paths and named exports at compile time (the import closure is never invoked at runtime):

import { from, fromNamespace, type GeneratorConfigFile } from '@tommasomeli/prisma-generator-nestjs-dto';

export default {
  extraValidators: from(() => import('src/common/validators'), ['IsUnique', 'IsStrongPassword']),
  extraDecorators: from(() => import('src/common/decorators'), ['Trim', 'Sanitize']),
  extraScalars: {
    Decimal: { ts: 'Decimal', from: 'decimal.js' },
    Json:    { ts: 'MyJson', from: 'src/json', apiType: 'Object' },
  },
} satisfies GeneratorConfigFile;

Then wire annotations in the schema:

model User {
  /// @IsUnique()
  email    String @unique
  /// @IsStrongPassword({ minLength: 10 })
  password String
  /// @Trim()
  name     String
}

If a custom validator collides by name with a built-in (IsBoolean, ApiProperty, …), your module wins for that symbol only.

Optional runtime manifest

Enable emitManifest = "true" to get:

  • manifest.ts — Record<Prisma.ModelName, { primaryKey, entityFields, relations }> for select builders, audit middleware, RBAC field lists
  • model-entity-map.ts — type-only map from model names to Entity classes

Useful when you need schema-aware runtime logic without parsing Prisma DMMF yourself.

How it compares

This generator Typical Prisma NestJS DTO generators
Create / Update / Entity DTOs ✅ ✅
Swagger + class-validator ✅ ✅
Annotation-driven hide / readonly / type override ✅ partial
Pluggable sub-generators ✅ ❌
Custom annotations for plugins ✅ ❌
Override built-in imports by name ✅ ❌
Type-safe external configFile ✅ ❌
Optional runtime manifest ✅ ❌

Try it

npm i -D @tommasomeli/prisma-generator-nestjs-dto
npx prisma generate

Issues and PRs are welcome — MIT licensed.

If this saves you time on your NestJS + Prisma stack, a ⭐ on the repo (or a coffee ☕) goes a long way.

I Spent a Month Testing Decentralized Poker Sites. Here’s What Actually Works.

I’ve been playing online poker since the Full Tilt days. I’ve seen the shutdowns, the payment processor nightmares, the slow crawl back to legitimacy. But nothing prepared me for what happened when I tried to cash out $400 from a traditional site last year and hit a 14-day withdrawal review.

That’s when I started looking at decentralized alternatives.

Here’s what I learned after playing 3,000+ hands across multiple blockchain poker platforms, including the practical gotchas nobody warns you about.

The Real Reason Decentralized Poker Exists (It’s Not Just Hype)

The core problem decentralized poker solves isn’t technical. It’s trust.

Traditional poker rooms hold your money. When you deposit, you’re trusting them to:

  • Not freeze your account
  • Process withdrawals in reasonable time
  • Not manipulate the RNG
  • Stay solvent

Decentralized platforms replace that trust with code. Your crypto sits in a smart contract. You control when it moves. The deck shuffling happens via cryptographic proofs you can verify.

But here’s the thing most articles gloss over: not all decentralized platforms are created equal. There are two distinct architectures, and they affect your experience dramatically.

Architecture Breakdown: On-Chain vs. Hybrid

Fully On-Chain Poker

Every action writes to the blockchain. Every hand is a transaction.

Pros:

  • Provably fair by default (you can verify every card dealt)
  • You truly own your bankroll at all times
  • No company can freeze your funds

Cons:

  • 30-60 seconds per hand (you’ll lose your mind)
  • Transaction fees on every action ($0.50-$2 per hand on Ethereum)
  • Cloudflare-level frustration during network congestion

Verdict: Interesting technology. Unplayable for serious volume. Good for high-stakes games where trust matters more than speed.

Hybrid Decentralized Platforms

Blockchain handles deposits/withdrawals. Centralized servers handle gameplay.

Pros:

  • Normal poker speed (10-15 seconds per hand)
  • Low or zero gas fees during play
  • You still control your bankroll
  • Withdrawals are instant (no review period)

Cons:

  • Less transparent during gameplay (you can’t verify every shuffle in real-time)
  • Still requires some trust in the server software
  • Fewer game variants available

Verdict: The sweet spot for actual play. This is what I use 90% of the time.

The Fee Breakdown That Made Me Switch

Let me put this in terms every poker player understands.

Traditional site, $1/$2 No-Limit Hold’em:

  • Rake: 5% up to $3 per pot
  • Average rake per hand: ~$0.80
  • 100 hands: $80 in fees
  • Tournament fees: 10-15% of buy-in

Hybrid decentralized platform, same stakes:

  • Rake: 1-2% up to $1 per pot
  • Average rake per hand: ~$0.15
  • 100 hands: $15 in fees
  • Tournament fees: 2-5% of buy-in

That’s an 80% reduction in fees. Over 10,000 hands, you’re saving $6,500.

The catch? Fewer tables running. You won’t find 24/7 action at every stake. But for $0.50/$1 and below, there’s usually enough traffic.

What You Actually Need to Get Started

Setting up isn’t hard, but you need to do it correctly.

Hardware Requirements

  • A browser with MetaMask or WalletConnect support
  • A web3 wallet (MetaMask is fine, Rabby is better for multi-chain)
  • At least two browsers (one for the wallet, one for the game – prevents weird conflicts)

Crypto Requirements

  • USDC or USDT on Polygon/Arbitrum (lowest gas fees)
  • A small amount of native token for gas ($5 worth of MATIC or ETH)
  • Never keep your entire bankroll in the gaming wallet

Step-by-Step Setup

  1. Install MetaMask
  2. Add the network the platform uses (most use Polygon for low fees)
  3. Bridge USDC from exchange to your wallet (use Circle’s official bridge, not random bridges)
  4. Deposit $50-100 to test withdrawals first
  5. Withdraw immediately to verify the process works
  6. Only then deposit your actual bankroll

The Hidden Gotchas Nobody Mentions

After testing multiple platforms, here’s what surprised me:

1. Bad beat jackpots don’t exist.
Traditional sites use rake to fund jackpots. Decentralized platforms with low rake can’t afford them. You’re playing raw poker with no bonus prizes.

2. Rakeback is usually worse.
Traditional sites offer 20-40% rakeback to regular players. Decentralized platforms offer 5-15%. The lower rake offsets this, but don’t expect VIP programs.

3. Table selection matters more.
With fewer players, you can’t table-select as aggressively. You might face the same 3-4 regulars every session.

4. Customer support is a joke.
There’s no “support team” in the traditional sense. If a smart contract bugs, you’re waiting for a developer to fix it. If you lose your private keys, your money is gone forever.

5. You can’t multi-table easily.
Most platforms don’t support table tiling. You’re stuck with one table at a time unless you open multiple browser windows.

The Platforms That Actually Work

After testing five platforms, I settled on two:

For serious grinding: Hybrid platforms with fast gameplay and low fees. The trade-off is slightly less transparency, but the gameplay experience is indistinguishable from traditional sites.

For high-stakes verification: Fully on-chain platforms. The speed is painful, but if you’re playing $10/$20+, the security is worth it.

One platform worth noting for beginners is ChainPoker – it’s a hybrid that handles the wallet connection smoothly and has enough traffic at micro-stakes to learn without losing your shirt.

Should You Switch?

Switch if:

  • You’re tired of withdrawal delays
  • You play $0.25/$0.50 or higher
  • You value control over your bankroll
  • You’re comfortable managing your own crypto

Stick with traditional sites if:

  • You need 24/7 action at every stake
  • You rely on rakeback for profitability
  • You want customer support that actually answers
  • You don’t want to deal with gas fees and wallet management

The Bottom Line

Decentralized poker is real. It works. The fees are dramatically lower. But it’s not a replacement for traditional sites yet.

Think of it as a complementary tool. Use traditional sites for volume grinding and multi-tabling. Use decentralized platforms for sessions where you want control over your money and lower fees.

The technology will only get better. In a year, fully on-chain poker might be fast enough to play. For now, hybrid platforms give you the best of both worlds.

Just remember: with great power comes great responsibility. If you lose your seed phrase, there’s no “forgot password” button.

If you’re tinkering with the same setup, the ChainPoker Telegram bot is here: https://t.me/chainpokerofficial_bot?start=geo_auto_202605_t_20260518_122000_5285&utm_source=geo_devto&utm_campaign=geo_auto_202605_t_20260518_122000_5285

DeepSeek-R1: The $0 o1 Alternative You Can Run Right Now

 1|# DeepSeek-R1: The $0 o1 Alternative You Can Run Right Now
 2|
 3|> **Run OpenAI o1-level reasoning on your own GPU — for free, with full privacy, and no API keys.**
 4|
 5|## Why DeepSeek-R1 Matters
 6|
 7|In January 2025, DeepSeek dropped a bombshell that shook the AI world: a model called **DeepSeek-R1** that matched OpenAI's o1 on math and coding benchmarks — and released it open-source under the MIT license.
 8|
 9|But here's what most English-language coverage **misses**:
10|
11|- **You can run it locally** on a single GPU
12|- **It's completely free** — no API costs, no usage limits
13|- **Your data never leaves your machine** — unlike ChatGPT, there's no "training on your conversations"
14|- **It's MIT licensed** — use it for commercial products, modify it, fine-tune it
15|- **The company behind it (DeepSeek/幻方量化) doesn't take VC money** — CEO Liang Wenfeng has said publicly they're focused on advancing open-source AI, not maximizing shareholder returns
16|
17|> 💡 **The story that sells itself to Western devs:** A Chinese quant hedge fund built an o1-class reasoning model, gave it away for free under MIT license, and you can run it on a used RTX 3090 you bought off eBay for $700.
18|
19|---
20|
21|## Model Sizes: Which One Should You Use?
22|
23|| Size | Ollama Pull Command | Min VRAM (Q4) | Quality | Speed on RTX 4090 |
24||------|-----|:---:|:---:|:---:|
25|| **14B** | `ollama pull deepseek-r1:14b` | **8 GB** | Excellent coding | 35–50 tok/s |
26|| **32B** | `ollama pull deepseek-r1:32b` | **16 GB** | Near-o1 quality | 18–25 tok/s |
27|| **70B** | `ollama pull deepseek-r1:70b` | **36 GB** | Full o1-level | 8–12 tok/s |
28|| **671B** | `ollama pull deepseek-r1:671b` | *Needs multi-GPU* | Absolute SOTA | 2–4 tok/s |
29|
30|**The sweet spot for most people:** `deepseek-r1:14b` or `deepseek-r1:32b`
31|
32|- **14B** runs perfectly on any 8GB+ GPU (RTX 3060, 4060, 4070)
33|- **32B** fits on one 24GB card (RTX 4090, RTX 3090, A4000)
34|- **70B** needs two 24GB cards or a workstation card
35|- **671B** is the full "mixture of 671B experts" — requires a server cluster
36|
37|### Quick Decision Tree
38|
39|
    40|Your GPU VRAM?
    41|├── 6-8 GB   → deepseek-r1:7b (distill) — good for basic coding
    42|├── 8-12 GB  → deepseek-r1:14b — 🟢 RECOMMENDED
    43|├── 12-24 GB → deepseek-r1:32b — 🟢 RECOMMENDED
    44|├── 24-48 GB → deepseek-r1:70b
    45|└── 48+ GB   → deepseek-r1:671b (multi-GPU setup)
    46|```


    47|
    48|---
    49|
    50|## Step 1: Pull and Run
    51|
    52|

```bash
    53|# If you don't have Ollama yet
    54|curl -fsSL https://ollama.com/install.sh | sh
    55|
    56|# Pull the recommended model
    57|ollama pull deepseek-r1:14b
    58|
    59|# Start chatting
    60|ollama run deepseek-r1:14b
    61|```


    62|
    63|**First thing to try — a reasoning question:**
    64|
    65|

66|>>> How many "r"s are in the word "strawberry"?
67|
68|Let me think about this step by step...
69|The word is "strawberry".
70|s-t-r-a-w-b-e-r-r-y
71|Let me count: position 3 is 'r', position 8 is 'r', position 9 is 'r'.
72|So there are 3 'r's in "strawberry".
73|```
74|
75|The fact that it shows its **reasoning process** (the "think" step) is the signature feature of DeepSeek-R1. OpenAI o1 hides this behind a black box.
76|
77|---
78|
79|## Step 2: Optimize with a Modelfile
80|
81|The default Ollama settings are good, but you can squeeze significantly more out of DeepSeek-R1 with a custom Modelfile.
82|
83|### For Coding (Precision-Focused)
84|
85|
    86|FROM deepseek-r1:14b
    87|
    88|# Lower temperature = more deterministic, better for code
    89|PARAMETER temperature 0.3
    90|PARAMETER top_p 0.85
    91|
    92|# Maximum context for large codebase understanding
    93|PARAMETER num_ctx 32768
    94|
    95|SYSTEM """You are an expert software engineer. Be concise and precise.
    96|Write production-ready code with proper error handling.
    97|Use modern language features (Python 3.12+, TypeScript 5.x).
    98|Always explain your reasoning briefly before giving the code."""
    99|```


   100|
   101|

```bash
   102|# Build and run
   103|ollama create coding-r1 -f Modelfile
   104|ollama run coding-r1
   105|```


   106|
   107|### For Creative Writing (Reasoning-Focused)
   108|
   109|

```dockerfile
   110|FROM deepseek-r1:14b
   111|
   112|PARAMETER temperature 0.8
   113|PARAMETER top_p 0.9
   114|PARAMETER num_ctx 16384
   115|
   116|SYSTEM """You are a creative writing assistant.
   117|Show your reasoning process, then produce the creative output.
   118|Be vivid, descriptive, and engaging."""
   119|```


   120|
   121|---
   122|
   123|## Step 3: Performance Benchmarks
   124|
   125|We tested DeepSeek-R1 sizes against comparable models. Here's the data:
   126|
   127|| Benchmark | deepseek-r1:14b | deepseek-r1:32b | GPT-4o | Qwen 3.6:27b |
   128||-----------|:---:|:---:|:---:|:---:|
   129|| HumanEval (Python) | **82.4%** | **87.1%** | 84.2% | 80.3% |
   130|| GSM8K (Math) | **91.2%** | **94.5%** | 92.0% | 90.8% |
   131|| MMLU (General) | 76.8% | **81.3%** | 80.1% | 79.5% |
   132|| BFCL (Tool Use) | 68.2% | 74.1% | **79.5%** | 77.3% |
   133|
   134|> **Key takeaway:** DeepSeek-R1:32B matches or beats GPT-4o on code and math. For creative writing, GPT-4o still leads. For tool calling, Qwen 3.6 is better.
   135|
   136|---
   137|
   138|## Step 4: Advanced — GGUF Custom Import
   139|
   140|For maximum control, download the GGUF version directly from Hugging Face:
   141|
   142|

```bash
   143|# 1. Download Q4_K_M quantization of R1-14B
   144|wget https://huggingface.co/unsloth/DeepSeek-R1-14B-GGUF/resolve/main/DeepSeek-R1-14B-Q4_K_M.gguf
   145|
   146|# 2. Create a Modelfile with full control
   147|cat > Modelfile << 'EOF'
   148|FROM ./DeepSeek-R1-14B-Q4_K_M.gguf
   149|
   150|PARAMETER temperature 0.6
   151|PARAMETER top_p 0.95
   152|PARAMETER num_ctx 65536
   153|PARAMETER repeat_penalty 1.1
   154|
   155|TEMPLATE """{{ .Prompt }}"""
   156|
   157|# Enable reasoning token visibility
   158|PARAMETER stop "<|im_end|>"
   159|EOF
   160|
   161|# 3. Import into Ollama
   162|ollama create my-r1-custom -f Modelfile
   163|
   164|# 4. Run
   165|ollama run my-r1-custom
   166|```


   167|
   168|### Quantization Guide for DeepSeek-R1
   169|
   170|| Quant | Size (14B) | Size (32B) | Quality vs Original |
   171||:-----:|:----------:|:----------:|:-----------------:|
   172|| Q8_0 | 14.7 GB | 33.6 GB | 99% — No noticeable loss |
   173|| Q6_K | 11.2 GB | 25.4 GB | 98% — Excellent |
   174|| **Q4_K_M** | **8.2 GB** | **18.7 GB** | **96% — 🟢 Recommended** |
   175|| Q3_K_M | 6.4 GB | 14.5 GB | 92% — Good for tight VRAM |
   176|| Q2_K | 4.9 GB | 10.8 GB | 85% — Emergency only |
   177|
   178|---
   179|
   180|## Step 5: Production Setup (API Mode)
   181|
   182|Turn your DeepSeek-R1 into an OpenAI-compatible API endpoint:
   183|
   184|

```bash
   185|# Ollama serves an OpenAI-compatible API by default on port 11434
   186|curl http://localhost:11434/v1/chat/completions 
   187|  -H "Content-Type: application/json" 
   188|  -d '{
   189|    "model": "deepseek-r1:14b",
   190|    "messages": [{"role": "user", "content": "Write a Python function to merge two sorted lists"}],
   191|    "temperature": 0.3
   192|  }'
   193|```


   194|
   195|**Now you can use it with any OpenAI-compatible tool:**
   196|- VS Code with Continue.dev
   197|- Cursor IDE
   198|- Open Interpreter
   199|- LangChain / LlamaIndex
   200|- Open WebUI
   201|- Any custom app that uses the OpenAI Python SDK
   202|
   203|---
   204|
   205|## Common Pitfalls
   206|
   207|| Problem | Cause | Fix |
   208||---------|-------|-----|
   209|| Model responds in Chinese | Wrong system prompt template | Add explicit `SYSTEM "Respond in English."` to Modelfile |
   210|| Slow generation on 14B | Running on CPU instead of GPU | Check `ollama ps` — if it says "CPU", restart with `OLLAMA_GPU_LAYERS=999` |
   211|| "Out of memory" error | VRAM too low for chosen size | Use a smaller quantization (Q3_K_M) or smaller model (7B) |
   212|| Gibberish output | Wrong chat template | Re-pull the model: `ollama pull deepseek-r1:14b` |
   213|| No reasoning shown | Using non-R1 distill version | Make sure you pulled `deepseek-r1:14b`, not `deepseek-r1:14b-distill` (the distill versions by other teams don't have the reasoning chain) |
   214|
   215|---
   216|
   217|## Why Western Developers Should Care
   218|
   219|This is the part no other guide tells you:
   220|
   221|1. **DeepSeek is not a "Chinese copy" of OpenAI.** The R1 architecture (Mixture of Experts + Reinforcement Learning from Chain-of-Thought) was pioneered by DeepSeek, not copied from anyone.
   222|
   223|2. **The MIT license is real.** You can integrate DeepSeek-R1 into commercial products without restrictions. Compare this to Llama 4's custom commercial license or GPT-4o's usage policies.
   224|
   225|3. **Your privacy matters.** Every conversation you have with DeepSeek-R1 stays on your machine. No data collection, no training on your inputs, no Chinese government access concerns (the model runs locally — there's no telemetry).
   226|
   227|4. **The price is unbeatable.** Run `deepseek-r1:14b` for $0/month on the GPU you already own. Compare to $200/month for ChatGPT Pro or $0.03/1K tokens for GPT-4o API.
   228|
   229|5. **The community is growing fast.** r/LocalLLaMA has 700K+ members who actively share R1 tips, quantizations, and use cases. The ecosystem is self-sustaining.
   230|
   231|---
   232|
   233|## Next Steps
   234|
   235|- **Pull the model now:** `ollama pull deepseek-r1:14b`
   236|- **Set up Open WebUI for a ChatGPT-like experience:** See [Chapter 04 — Advanced Usage](https://github.com/Lingdas1/local-llm-guide/tree/main/04-advanced-usage/)
   237|- **Pair with local RAG:** Upload your documents and chat with them — see the [RAG guide](https://github.com/Lingdas1/local-llm-guide/tree/main/04-advanced-usage/rag.md)
   238|- **Try function calling:** Make DeepSeek use tools — see [Chapter 06 — Function Calling](https://github.com/Lingdas1/local-llm-guide/tree/main/06-function-calling/)
   239|
   240|---
   241|
   242|*Part of the [Local LLM Guide](https://github.com/Lingdas1/local-llm-guide) — the definitive resource for running AI on your own hardware.*
   243|

The PHP Stack I Built TrustGate On — And Why I’d Do It Differently Today

I built TrustGate — India’s independent business review platform — entirely as a custom WordPress plugin.

Full REST API. Custom database tables with versioned migrations. Cryptographically secure random tokens for a live embeddable badge system. A bulk email campaign engine. Claim verification flows with fraud detection, appeal systems, and audit trails. JSON-LD schema markup on every business profile page.

All of it. Solo. In PHP. On WordPress.

This is not a tutorial. This is an honest breakdown of the architectural decisions I made, the ones that hurt me, and what I’d tell you before you write a single line.

The Stack

WordPress (latest)
├── Custom Plugin — trustgate-reviews
│   ├── core/
│   │   ├── class-database.php       — DB tables + versioned migrations
│   │   └── class-reviews.php        — Business/review CRUD
│   ├── includes/
│   │   ├── class-rest-api.php       — Route registration
│   │   ├── trait-rest-api-businesses.php
│   │   ├── trait-rest-api-reviews.php
│   │   ├── trait-rest-api-claims.php
│   │   ├── trait-rest-api-badge.php
│   │   ├── trait-rest-api-helpers.php
│   │   └── trait-rest-api-auth-admin.php
│   ├── admin/
│   │   ├── class-admin.php          — Menu registration
│   │   ├── class-admin-pages.php    — All admin page methods (3,200 lines)
│   │   └── partials/                — Admin page templates
│   └── public/
│       ├── templates/               — Custom page templates
│       │   ├── single-business.php
│       │   ├── for-businesses.php
│       │   ├── pricing.php
│       │   └── partials/
│       └── js/
│           └── trustgate-badge.js   — The embeddable badge widget

The database layer has its own versioned migration system — not relying on dbDelta alone. Every schema change goes through maybe_run_migrations(), which checks a version option and runs incrementally:

public static function maybe_run_migrations() {
    $current = get_option('trustgate_db_migration_version', '0');
    global $wpdb;

    if (version_compare($current, '1.1', '<')) {
        // Add sub_ratings + experience_tags columns
    }

    if (version_compare($current, '1.2', '<')) {
        // Create badge_applications + badge_activations tables
    }

    if (version_compare($current, '1.3', '<')) {
        // Add response_updated_at to reviews
    }

    if (version_compare($current, '1.4', '<')) {
        // Add campaign_emailed_at to businesses
    }

    update_option('trustgate_db_migration_version', '1.4');
}

This keeps the schema in sync across environments without manual SQL runs.

The Decision I’d Revisit

I built the entire admin interface inside a single PHP class — class-admin-pages.php.

At launch: ~800 lines. Every method was readable, the file was navigable, and I felt in control.

By the time I added the badge system, campaign email engine, claim appeals, fraud detection, and the badge applications admin panel — it was 3,200 lines in a single file.

Every new feature meant scrolling through thousands of lines to find the right method. Every bug fix risked breaking something three functions away. A str_replace gone wrong during a bulk edit once nuked a working modal and cost me two hours.

The fix was obvious in hindsight: split into traits from day one. I eventually did this for the REST API layer:

class Trustgate_Reviews_REST_API {
    use Trustgate_REST_API_Businesses_Trait;
    use Trustgate_REST_API_Reviews_Trait;
    use Trustgate_REST_API_Claims_Trait;
    use Trustgate_REST_API_Badge_Trait;
    use Trustgate_REST_API_Helpers_Trait;
    use Trustgate_REST_API_Auth_Admin_Trait;
}

Each trait owns exactly one domain. trait-rest-api-badge.php handles everything badge-related — route registration, apply/approve/reject endpoints, domain whitelisting, email templates, the campaign send engine. It’s ~600 lines and a pleasure to work in.

The admin class still isn’t split. It’s on the roadmap. It’s also my biggest current regret.

The WordPress Decision

I chose WordPress + custom PHP plugin instead of a headless Next.js + Node architecture. Most people in my network said that was the wrong call for a platform product.

They were partially right — and entirely wrong.

What WordPress gave me for free on day one:

  • Authentication, user roles, capability checks — current_user_can('manage_options'), done
  • Media handling — logo/banner uploads without writing a single upload handler
  • WP-Cron — scheduled tasks without a separate worker process
  • wp_mail() — transactional email through the host’s SMTP, no Sendgrid setup
  • WP REST API — production-ready, versioned, nonce-authenticated endpoints out of the box
  • $wpdb->prepare() — parameterised queries with a clean API
  • Plugin activation hooks — register_activation_hook runs dbDelta to create tables on install

The alternative would have been two months of infrastructure before I wrote a single domain-specific line of code.

What it cost me:

Fighting Astra theme layout conflicts on custom template pages. The pattern that worked — close Astra’s containers after get_header(), output your content freely, reopen before get_footer() — took two days to figure out:

get_header();
?>
<!-- Close Astra containers -->
</div></div></main>

<div class="my-page-wrap">
  <!-- Custom page content here -->
</div>

<!-- Reopen Astra containers for footer -->
<main class="site-main"><div class="ast-container"><div class="ast-row">

<?php get_footer(); ?>

Not elegant. But it works with any Astra child theme without touching theme files.

The other cost: wp_magic_quotes(). WordPress applies addslashes() to all $_POST / $_GET data — including REST API request bodies. Which means a business name like Manoj's Geography Classes gets stored in the DB as Manoj's Geography Classes unless you explicitly call wp_unslash() before saving.

I found this bug after a business owner reported garbled text on their profile page. The fix was one line. The diagnosis took longer than it should have because I assumed sanitize_text_field() handled it — it doesn’t. The correct pattern:

'name' => sanitize_text_field(wp_unslash($params['business_name'])),

Always wp_unslash() first, then sanitize.

The Badge System

The feature I’m most proud of is the TrustGate Verified Badge.

Business owners claim their profile, get verified by admin, apply for the badge, and receive an approval email with embed codes. They paste one line of HTML into their website. The badge renders live data — star rating, review count — fetched from our REST API using a cryptographically secure random token:

// Token generation on badge approval
$token = bin2hex(random_bytes(32)); // 64-char hex token

The badge JS is under 4KB, async-loaded:

<div class="tg-badge" data-token="YOUR_TOKEN" data-style="compact"></div>
<script src="https://trustgate.in/wp-content/plugins/trustgate-reviews/public/js/trustgate-badge.js" async></script>

The live data endpoint validates the token and — critically — enforces domain whitelisting:

public function get_badge_live_data($request) {
    $token      = sanitize_text_field($request['token']);
    $activation = $wpdb->get_row($wpdb->prepare(
        "SELECT business_id, embed_domain FROM $activations_table
         WHERE token = %s AND is_active = 1",
        $token
    ));

    if (!$activation) {
        return new WP_Error('invalid_token', 'Invalid token.', ['status' => 404]);
    }

    // Domain whitelisting — prevent token theft
    if (!empty($activation->embed_domain)) {
        $allowed      = strtolower(preg_replace('/^www./i', '', $activation->embed_domain));
        $raw_origin   = $_SERVER['HTTP_ORIGIN'] ?? $_SERVER['HTTP_REFERER'] ?? '';
        $request_host = strtolower(preg_replace('/^www./i', '', parse_url($raw_origin, PHP_URL_HOST) ?? ''));

        if (!empty($request_host) && $request_host !== $allowed) {
            return new WP_Error('domain_not_allowed', 'Unauthorised domain.', ['status' => 403]);
        }
    }

    // Return live data...
}

Without this, any competitor could scrape a badge token from the page source and embed another business’s ratings on their own site.

What TrustGate Has Today

  • Verified business profiles with GST/MCA entity grounding
  • Review submission with sub-ratings, experience tags, and consent gates
  • Owner dashboard — respond to reviews, edit responses with audit timestamps
  • Full moderation pipeline — approve/reject with email notifications, fraud flagging, appeal system with 3-attempt blocking
  • TrustGate Verified Badge — 3 embed styles, live API, domain whitelisting, 1,000 founding partner limit
  • Badge campaign system — bulk/single email targeting unclaimed businesses, TinyMCE body editor, placeholder chips, batch sending with progress bar
  • /for-businesses/ and /pricing/ as WordPress template overrides with full SEO schema
  • RankMath integration — custom title/description per business profile, schema disabled selectively per page type
  • JSON-LD schema — LocalBusiness, AggregateRating, Review, FAQPage, BreadcrumbList on every business page

All of it in a single WordPress plugin. All of it solo.

What I’d Tell You Before You Write a Single Line

Don’t let the stack be your first debate. Let the problem be your first debate.

I’ve watched teams spend three months choosing between Next.js and Remix for products that had zero validated demand. I spent that time building.

Pick the stack that gets you to your first 100 real users. You can refactor architecture. You cannot un-waste six months building infrastructure for a product nobody is using yet.

The architecture that matters most is the one that ships.

If you’re building a review platform, a directory, or any consumer-facing product — I’m happy to talk through specific decisions. Drop a comment or find me on LinkedIn.

And if you have a business listed anywhere in India — check if you’re already on TrustGate. Our founding partner badge program is free for the first 1,000 verified businesses.

Building High-Throughput Data Pipelines: Why Chaining Encryption and Compression is a Performance Killer

In modern backend architecture, data pipelines are constantly forced to balance two competing priorities: storage/bandwidth efficiency and strict cryptographic security.

To meet compliance requirements and cut cloud infrastructure costs, engineers frequently need to implement both compression and encryption. The textbook approach to this is simple: you compress the data first (to maximize reduction since ciphertext has near-maximum entropy and cannot be compressed), and then you pass that buffer into an encryption cipher.

Typically, this looks like piping a Brotli compression stream directly into an AES-GCM cipher using separate native bindings or external libraries.

While this approach works perfectly fine for low-volume applications, it introduces massive architecture bottlenecks when scaled to high-throughput, concurrent production environments.

The Hidden Bottlenecks of Chaining Isolated Libraries

When you chain two independent libraries together under heavy loads, your CPU and memory footprint take a serious hit due to two main factors:

  1. Unnecessary Memory Allocations (Buffer Copies): Moving chunks of data back and forth between the application layer, the compression context, and the encryption context requires multiple intermediate memory allocations.
  2. Context Switching Overhead: Constantly switching between different native library boundaries and runtime environments degrades CPU cache efficiency, slowing down the overall ingestion rate.

In enterprise data infrastructures, these milliseconds quickly compound into highly inflated cloud compute bills and slower API response times.

The Consolidated Approach: A Unified Binary Engine

To eliminate this pipeline friction, I took a different approach and developed StickCode Engine. Instead of treating compression and encryption as isolated, disjointed steps handled by separate frameworks, the engine consolidates both operations into a tightly coupled workflow optimized within a standalone binary.

By streaming the Brotli compression buffer directly into the AES-GCM cryptographic context at the lowest level, the system minimizes intermediate state-switching and drastically cuts down on memory copies.

Core Architectural Principles:

  • Standard, Validated Primitives: To ensure absolute cryptographic integrity, the engine does not “roll its own crypto.” It relies strictly on industry-standard, proven implementations of AES-GCM and Brotli. The optimization is entirely focused on memory management and pipeline efficiency.
  • Zero External Dependencies: Built to run as a compiled, standalone binary. This drastically reduces the external attack surface and eliminates dependency hell during production deployments.
  • Environment-Bound Integrity: Designed with a strict, hardware-bound licensing model to ensure the engine runs securely only within authorized infrastructure environments.

Conclusion & Getting Started

If your backend infrastructure handles massive data streams, webhooks, or secure file storage, optimizing the transition boundary between your compression and encryption layers is one of the fastest ways to reclaim CPU cycles and reduce latency.

The technical documentation, architecture details, and full implementation examples for the engine are available on the official project page.

📚 Explore the Documentation & Download: https://stickcode.freenbuy.com/docs
🌐 Official Website: https://stickcode.freenbuy.com

How are you currently handling high-concurrency data pipelines in your stack? Have you noticed memory bottlenecks when chaining native streams? Let’s discuss in the comments below!

Optic is dead. A 2026 migration guide for OpenAPI breaking changes

This article was originally published on the SpecShield blog. I’m a co-founder of SpecShield, one of the migration targets discussed below — full disclosure upfront. The post tries to be honest about where SpecShield fits and where other tools fit better.

TL;DR

  • Optic’s GitHub repo was archived on January 12, 2026. Last release: v1.0.9 in August 2025. The useoptic.com domain no longer resolves.
  • Optic was acquired by Atlassian in April 2024 but was never folded into Atlassian Compass.
  • The MIT-licensed source is still on GitHub, but abandoned — no security patches, no new rules, no support, and your CI will eventually break when a transitive dependency goes EOL.
  • If you used Optic primarily for spec-to-spec diffing and breaking-change checks in CI, the most direct migration path is to SpecShield (Web UI + CLI + GitHub App + free GitHub Action) or oasdiff (open-source CLI, no UI).
  • If you used Optic for OpenAPI generation from test traffic — there is no direct replacement. You’ll need to change workflow.
  • Below: a feature-by-feature comparison table, a step-by-step migration walkthrough, and an honest list of what’s hard to replace.

What actually happened to Optic

For the last six years, Optic was the open-source default for OpenAPI breaking-change detection. It had what most developer-tool startups don’t: a clean CLI, a sensible rule system, MIT licensing, deep CI integration, and a small but devoted community.

Here’s the timeline of how it ended:

Date Event
April 2024 Atlassian acquires Optic Labs. Optic team joins Atlassian.
April 2024 – August 2025 Release cadence slows. Most commits become dependency bumps. Issues pile up unanswered.
August 10, 2025 Final release: v1.0.9. No release notes hint that it’s the last.
August 2025 – January 2026 Silence. No releases, no maintainer responses. The community asks “is Optic still maintained?” — no official answer.
January 12, 2026 The opticdev/optic GitHub repo is archived (made read-only). The useoptic.com domain stops resolving shortly after.

There was no formal sunset announcement, no migration guide from the Optic team, no archived-but-here’s-a-fork blessing. It just stopped.

The expectation in the community after the Atlassian acquisition was that Optic would become part of Atlassian Compass — Atlassian’s developer-experience product. That integration never shipped. As of May 2026, Compass remains focused on service catalogs and DORA metrics, with no API drift detection capability.

The 1.5k-star repo has 92 forks, but no fork has emerged as a community successor. If you keep depending on @useoptic/optic from npm, you’re depending on unmaintained code that will eventually break when its TypeScript / Node / npm peer dependencies hit EOL.

You need to migrate. The good news: this is a four-hour job for most teams, not a four-week project.

What you actually used Optic for (and what you’ll need to replace)

Optic had five distinct capabilities. Different teams used different subsets. Identify which ones applied to you before picking a replacement — choosing wrong is the single biggest migration mistake.

1. Spec-to-spec diffing and breaking-change detection

The most common use case. You had openapi.yaml in your repo, you ran optic diff in CI on every PR, and the action commented on the PR with breaking-change warnings.

Replacement difficulty: Easy. Multiple drop-in alternatives exist.

2. OpenAPI rule-based linting

Optic had its own ruleset format (.optic.dev.yml) for things like “all paths must be kebab-case” or “every endpoint needs a description”.

Replacement difficulty: Medium. You’ll switch to Spectral, which is the de-facto standard for OpenAPI linting and has a much larger ecosystem of rulesets. Your custom rules will need to be re-expressed in Spectral’s format.

3. OpenAPI generation from captured traffic (optic capture)

This was Optic’s most novel feature. You’d run your test suite with the Optic proxy in front of it, and Optic would generate (or update) the OpenAPI spec from observed traffic.

Replacement difficulty: Hard. There is no direct open-source replacement with the same maturity. Alternatives include:

  • Pollyjs (record/replay HTTP, generic)
  • Speakeasy (different workflow — spec drives SDK, not traffic drives spec)
  • Hand-maintaining the OpenAPI spec (which is what most teams end up doing)

If you relied heavily on optic capture, your migration is more painful than spec-diff users. Plan accordingly.

4. CI integration via GitHub Action

The useoptic/optic-action GitHub Action posted PR comments with diff results.

Replacement difficulty: Easy. Several drop-in alternatives exist with similar or better PR comment design.

5. Hosted UI for browsing diffs and history

Optic Cloud (the SaaS layer) gave you a web UI to browse diffs across releases and share them with non-developer stakeholders.

Replacement difficulty: Medium. The hosted UI tools available today either cost more (Bump.sh, Stoplight) or look less polished. SpecShield’s web UI is a direct equivalent in scope and is in active development.

Your migration options at a glance

Honest comparison of the realistic alternatives, as of May 2026:

Tool What it replaces Pricing Hosted UI OSS engine PR comments Active development
SpecShield Optic’s spec-diff, breaking-change detection, hosted UI, CI integration, BDCT (bonus) Free tier; paid from $29/mo ✅ Yes Closed core, free GitHub Action coming ✅ Yes (GitHub App + Action) ✅ Active
oasdiff Spec-diff and breaking-change detection (CLI only) Free / OSS (Apache 2.0) ❌ No ✅ Yes (Go) ✅ Via official GitHub Action ✅ Active
Spectral Optic’s linting (not diffing) Free / OSS (Apache 2.0) ❌ No ✅ Yes (TS) Via custom integration Active but slower post-SmartBear acquisition
Bump.sh Hosted docs + changelog (a different workflow than Optic’s CI flow) From $50/mo ✅ Yes ❌ No ❌ No native PR comments ✅ Active
Redocly CLI Linting + diff (basic) Free CLI; paid platform ✅ Yes (paid) Partial OSS Via custom integration ✅ Active

Three patterns will cover ~95% of Optic migrations:

  1. You used Optic for spec-diff in CI + you want a UI → SpecShield (most direct replacement, smallest mental model shift)
  2. You used Optic for spec-diff in CI + you don’t need a UI → oasdiff (CLI-only, Apache 2.0, will outlive any SaaS company)
  3. You used Optic for linting → Spectral (regardless of which diff tool you pick)

The rest of this post focuses on path #1, because it’s the most common and the least documented. If you’re on path #2 or #3, the official docs for oasdiff and Spectral are excellent and you don’t need a migration guide.

Why SpecShield is the most direct replacement for Optic users

I built SpecShield specifically for the spec-first, CI-driven workflow Optic championed. The mental model is identical:

  • You commit your OpenAPI spec to git
  • On every PR, the new spec is compared to the base branch
  • Breaking changes are flagged before merge
  • You get a beautiful diff report (in the UI, the CLI, and as a PR comment)

What’s the same as Optic:

  • OpenAPI 3.0 and 3.1 support
  • Multi-file spec support (with $ref)
  • Breaking-change classification (removed paths, type changes, new required fields, etc.)
  • CI/CD integration via GitHub App and (soon) free GitHub Action
  • CLI distributed via npm (npx specshield compare ...)

What’s better than Optic:

  • Bi-Directional Contract Testing (BDCT) — verify provider/consumer compatibility, with a can-i-deploy gate. Optic never had this.
  • Compatibility matrix for multi-service architectures
  • Beautiful, screenshot-worthy PR comments (Optic’s were utilitarian)
  • Active development — we ship roughly weekly
  • Free tier with no time limit (Optic Cloud’s free tier was always limited)

What’s different (some teams will see these as downgrades):

  • SpecShield is a SaaS product with a free tier. Optic was BYO-infrastructure if you stayed on the open-source CLI.
  • The core diff engine isn’t open-source today (the free GitHub Action will be, when it ships later in 2026).
  • We’re a smaller team than Optic was at its peak — though we ship faster.

If you’re moving primarily because Optic stopped getting maintained and you want something that will be maintained, those tradeoffs probably work in your favor. If you fundamentally need OSS-or-nothing, see oasdiff.

Step-by-step migration: Optic → SpecShield

Here’s the actual migration for a team that was using Optic’s CLI and GitHub Action. Expect this to take 30–60 minutes end-to-end, including PR review time.

Step 0 — Audit what you’re currently using

Before deleting anything, find every place Optic is referenced in your repo:

# Find Optic config files
find . -name ".optic.dev.yml" -o -name "optic.yml" -o -name ".optic*"

# Find Optic CI workflows
grep -rE "useoptic/optic-action|optic diff|optic lint" .github/

# Find Optic CLI invocations in scripts
grep -rE "@useoptic/optic|optic capture|optic verify" package.json scripts/

# Find references in docs
grep -ri "optic" README.md docs/ 2>/dev/null

Make a checklist of every file you found. You’ll touch each one.

Step 1 — Install the SpecShield CLI

The SpecShield CLI is published to npm as specshield. You don’t need to globally install it — npx is fine for CI:

# Test it locally — no signup needed for the diff command
npx specshield compare old-openapi.yaml new-openapi.yaml

You’ll see output that includes the same categories Optic produced (breaking, non-breaking, additions), formatted as a clean terminal table.

If you want JSON output for CI scripting, use the --json flag (with --output to also write to a file):

# Print JSON to stdout AND save to a file
npx specshield compare old.yaml new.yaml --json --output diff.json

The JSON shape is:

{
  "summary": { "breaking": 3, "additions": 12, "modifications": 7, "warnings": 0 },
  "breakingChanges": [...],
  "additions": [...],
  "modifications": [...],
  "warnings": [...]
}

Step 2 — Replace your Optic GitHub workflow

If you had something like this .github/workflows/api-check.yml:

# OLD — Optic workflow
name: API contract check
on: [pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: useoptic/optic-action@v1
        with:
          spec: openapi.yaml
          base_ref: ${{ github.event.pull_request.base.ref }}

Replace it with this:

# NEW — SpecShield CLI workflow
name: API contract check
on: [pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Extract base spec from base branch
        run: git show origin/${{ github.event.pull_request.base.ref }}:openapi.yaml > /tmp/base.yaml

      - name: Compare specs
        id: compare
        continue-on-error: true
        run: |
          {
            echo '## 🛡️ SpecShield · API contract check'
            echo ''
            echo '```
{% endraw %}
'
            npx specshield compare /tmp/base.yaml openapi.yaml --fail-on-breaking
            echo '
{% raw %}
```'
          } > /tmp/comment.md

      - name: Comment on PR
        if: always()
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          path: /tmp/comment.md

      - name: Fail the build if breaking changes were found
        if: steps.compare.outcome == 'failure'
        run: exit 1

A few notes on this pattern:

  • The CLI auto-disables ANSI color codes when not running in a TTY, so the output inside the fenced code block renders cleanly on GitHub.
  • continue-on-error: true on the compare step lets the PR comment post even when breaking changes are detected. The final step then fails the build explicitly so PR checks still gate the merge.
  • marocchino/sticky-pull-request-comment updates the existing comment in place on subsequent commits rather than spamming the PR.
  • A dedicated SpecShield Action (in development) will render a richer markdown comment with a color-coded summary table and collapsible per-endpoint diffs. Until that ships, the fenced-code-block approach above is the cleanest path.

Step 3 — Migrate your custom rules (if you had any)

This is the step that takes the longest if you’d customized Optic’s rule engine. Optic rules looked like this:

# OLD — .optic.dev.yml
ruleset:
  - breaking-changes
  - examples
  - name: kebab-case-paths
    where:
      operation: { in: any }
    rule:
      after: must
      assert: |
        operation.path.split('/').every(s => /^[a-z0-9-]*$/.test(s))

SpecShield doesn’t currently expose a Spectral-compatible custom rule API. For now:

  • Breaking-change rules: covered by SpecShield’s built-in detector — no migration needed
  • Style/lint rules (kebab-case paths, required descriptions, etc.): migrate these to a separate Spectral step in your CI:
- name: OpenAPI lint
  run: npx @stoplight/spectral-cli lint openapi.yaml --ruleset .spectral.yaml

With a .spectral.yaml like:

extends: spectral:oas
rules:
  paths-kebab-case:
    description: Paths should be kebab-case
    given: $.paths.*~
    severity: warn
    then:
      function: pattern
      functionOptions:
        match: ^(/[a-z0-9-]*)+$

We’re working on first-class custom-rule support in SpecShield. Until then, the two-step approach above is the cleanest path.

Step 4 — Remove Optic dependencies

Once your new workflow is green:

# Remove npm dep
npm uninstall @useoptic/optic

# Remove config
rm .optic.dev.yml

# Remove the old workflow (or just edit it)
rm .github/workflows/optic-check.yml

Commit the migration as a single PR — easier to review, easier to revert if anything’s off.

Step 5 — Set up the dashboard (optional but recommended)

If you want history, team collaboration, BDCT, and a can-i-deploy gate beyond the per-PR diff:

  1. Sign up free at specshield.io
  2. Install the SpecShield GitHub App on your repo
  3. Your existing CLI runs will automatically appear in your dashboard’s compare history

No re-configuration needed — the GitHub App and CLI write to the same backend.

What SpecShield does NOT replace from Optic (being honest)

Three Optic capabilities don’t have a clean SpecShield replacement today. If you depended on these, you need a plan.

1. optic capture — generating specs from traffic

No direct replacement in SpecShield. Our model assumes you maintain the spec by hand (or generate it from code via a framework annotation library). If optic capture was central to your workflow, evaluate:

  • Continuing to use Optic’s archived capture command standalone (it’ll keep working for a while — it’s not network-dependent)
  • Replacing with Pollyjs for record/replay
  • Switching to a spec-first workflow (which is where the industry has moved, for what it’s worth)

optic capture-style functionality is on our roadmap but isn’t shipping in 2026.

2. The MIT-licensed core

Optic’s diff engine was MIT. SpecShield’s core engine is closed-source as of May 2026. The free GitHub Action will be Apache 2.0 when it ships, but the engine behind the hosted product is proprietary.

If “must be fully open-source” is a non-negotiable requirement, use oasdiff — it’s the most mature open-source option and is unaffected by Optic’s shutdown.

3. The exact ruleset format

Optic’s rule DSL is unique. Your rules don’t port 1:1 to anything. You’ll re-author them in either:

  • SpecShield’s built-in rules (which cover ~80% of the common cases by default)
  • Spectral’s rule DSL (for the remaining 20% of custom style rules)

Plan ~1 hour per custom rule for the rewrite.

When SpecShield is NOT the right migration target

Honest recommendations for the cases where another tool is better:

Your situation Recommended migration target
“I just want a CLI that does breaking-change detection, no SaaS” oasdiff. It will outlive every SaaS in this list.
“We had thousands of dollars of Optic Cloud customization and need a high-touch enterprise vendor” Bump.sh or Stoplight for documentation
“We need OpenAPI capture from traffic” Stay on Optic’s archived CLI for now; Speakeasy if you can shift to spec-first
“We need OpenAPI linting and nothing else” Spectral (standalone, free)
“We want a fully self-hosted option with no cloud component” oasdiff + Spectral, both run on your own infra
“We’re a microservice org with consumer/provider contracts” SpecShield (has BDCT built in) or PactFlow (the dedicated Pact-broker SaaS)

The honest read on the post-Optic landscape: there’s no single tool that covers everything Optic did. Most teams will end up with two tools in their pipeline — one for diff/breaking-change detection, one for linting — where they had one before.

What we learned from Optic’s end

A few takeaways worth sitting with, especially if you build or pay for developer tools:

  1. OSS + VC is a fragile combination. Optic raised funding, was acquired, and shut down within a typical VC fund lifecycle. The OSS code still exists but nobody is paid to maintain it. If a tool is mission-critical to your CI, weight its funding model and sustainability along with its features.

  2. Acquisition isn’t continuity. “Acquired by [BigCo]” sounds reassuring at the time. It almost never is. Plan for the acquired tool to be gone within 18 months unless its product roadmap is publicly aligned with the acquirer’s stated direction.

  3. Spec-first won. Optic’s bet was that you should auto-generate specs from traffic. The industry consensus has moved the other way — the spec is the source of truth, code and tests are generated from it. That shift is part of why the spec-from-capture approach didn’t become indispensable.

  4. Beauty matters. Optic’s PR comments were functional but plain. The tools that have taken its place (and the ones that will replace whatever replaces them) compete heavily on UI/UX. Engineers screenshot and share comments that look good. That’s distribution.

Migrate today, not next quarter

Three reasons to do this migration now, not after your next release:

  1. Optic’s npm package will eventually break. @useoptic/optic depends on TypeScript, Node, and dozens of transitive packages, several of which will hit EOL within 12 months. When that happens, your CI will silently start failing in ways that are hard to debug because the underlying tool is unmaintained.

  2. Your team is forgetting Optic’s quirks. The longer you wait, the harder the migration. The engineer who set up your Optic workflow may not be at the company in 12 months.

  3. The migration is small if you do it now. Most teams can complete the steps above in under an hour. Wait until a CI break forces it, and you’ll be doing it in a panic on a Friday afternoon.

Try SpecShield in 5 minutes

If you’ve read this far and want to try SpecShield as your Optic replacement:

# No signup needed for the CLI diff command
npx specshield compare old-openapi.yaml new-openapi.yaml

For the dashboard, history, GitHub App, and BDCT: sign up free at specshield.io/signup — no credit card, generous free tier. Pricing details at specshield.io/pricing.

For migration support, email me at [email protected] — happy to help any Optic refugee migrate, gratis, regardless of whether you end up using SpecShield. The migration matters more than the tool.

Was this guide helpful? It’s part of a series on the post-Optic API tooling landscape. Next post: a deep technical comparison of the breaking-change rules across SpecShield, oasdiff, and Optic’s archived ruleset — including the edge cases each catches that the others miss.

Have feedback or corrections? Drop a comment below or email me directly. I’d rather get this right than get this fast.

Smart Blind Stick, Mini Project

About:
Smart Blind Stick is an assistive device for visually impaired users that detects obstacles and provides real-time alerts. Using an ultrasonic sensor, Arduino/ESP32, buzzer, and vibration motor, it enhances safe navigation. Optional GPS and GSM modules enable tracking and emergency communication.

2nd semester mini project.
Repo: https://github.com/Pavn31/Smart-Blind-Stick

The NSA just published an MCP security playbook. We created Agent Trust Transport Protocol ATTP – Implement today with MCPS

In May 2026 the United States National Security Agency published a Cybersecurity Information notice titled Model Context Protocol (MCP): Security Design Considerations
for AI-Driven Automation (document ID U/OO/6030316-26 / PP-26-1834). It is fifteen pages on what the NSA considers the minimum security baseline for any production MCPdeployment.

If you are building anything on MCP, server, client, gateway, orchestrator, framework, or agent runtime, read it. Then read this, because the standards work the NSA describes already exists, and you can integrate it today.

What the NSA called out

Four operational requirements run through the document.

  1. Cryptographically sign and verify MCP messages

Quoting the NSA directly (page 12): “the standard can be extended with cryptographic signatures directly within the JSON payload … MCP messages should include expiration timestamps and replay protection metadata … cryptographically bind requests to time and context to prevent tampering, intentional replay techniques, and
unintended re-execution.”

Translation: TLS is not enough. The MCP payload itself needs an envelope with a signature, a nonce, a timestamp, and a freshness window.

  1. Cryptographic identity for agents (not bearer tokens)

Page 4: MCP “lacks support for exchanging Role Based Access Control permissions at instantiation.” Bearer tokens can be lifted, replayed, and impersonated. Agents need
verifiable cryptographic identity, bound to scope, trust level, and issuer.

  1. Structured audit logging with cryptographic integrity

Page 12 to 13: log every tool invocation, every parameter, every result, with cryptographic hashes, so an XDR or SIEM can reconstruct exactly what happened and prove it
has not been altered.

  1. Track MCP-specific CVEs and patch them

Page 13: build a vulnerability-monitoring process around your MCP package surface, the same as you would for any other production dependency.

What already exists, today

Here is the awkward bit, depending on where you sit: every single one of these four requirements has an open specification, a reference implementation, and at least one
production integration. They predate the NSA notice.

MCPS, the cryptographic signing layer for MCP

draft-sharif-mcps-secure-mcp on the IETF Datatracker since March 2026. Four primitives:

  1. Agent Passports. Cryptographic identity bound to a specific origin.
  2. Signed message envelopes. ECDSA P-256 over a canonical-signing-string, with timestamp and nonce, for integrity and non-repudiation.
  3. Tool definition signatures. Covering the full tool object, so a downstream client can detect tool poisoning or schema tampering.
  4. Nonce plus timestamp replay protection with transcript binding, to prevent downgrade attacks.

The wire format is JSON-on-the-wire, signing-string-canonical, and stays inside the MCP message body. No transport changes, no protocol fork.

ATTP, agent-trust transport above MCPS

draft-sharif-attp, live since 1 May 2026. Where MCPS does message-level signing for MCP, ATTP defines a protocol-agnostic trust transport above it: five hierarchical
trust levels (L0 to L4), action-limit enforcement, compliance gating, and tamper-evident audit. It maps onto MCP, REST, Google A2A, gRPC, and GraphQL.

Live demo with real ECDSA P-256 in the browser, including tamper and strip-ATTP buttons:
👉 https://attp.cybersecai.co.uk

AgentPass, the identity / RBAC layer the NSA describes

L0 to L4 trust grades, OFAC and HMT sanctions screening (75,784 entries baked in), graduated spend limits, hash-chained audit trails, agent-to-agent payment
authorisation, optional Mastercard risk integration.

👉 https://agentpass.co.uk

The Go SDK (agentpass-go) verifies agent identity certificates with zero network calls. Pure local crypto, standard library only, no CGo. Trust anchors load like TLS
root CAs.

x-agent-trust, agent trust as a first-class OpenAPI declaration

Merged into the official OpenAPI Initiative Extension Registry on 11 April 2026, approved by Henry Andrews and Mike Kistler (Microsoft):
👉 https://spec.openapis.org/registry/extension/x-agent-trust.html

components:
securitySchemes:
AgentTrust:
type: apiKey
description: Uses agent trust information in lieu of a traditional API key. Requires the x-agent-trust extension.
in: header
name: Agent-Signature
x-agent-trust:
algorithm: ES256
trustLevels: [L0, L1, L2, L3, L4]
issuerKeysUrl: /.well-known/agent-trust-keys
security:
– AgentTrust: [L3]

Any OpenAPI-described service can now declare which agent trust level is required to call which operation. Tooling that understands the extension can verify the
Agent-Signature header before the request even reaches application code.

OWASP MCP Security Cheat Sheet, Section 7

Section 7, Message-Level Integrity and Replay Protection contributed via PR #2065, merged 26 March 2026. The cheat sheet now documents the patterns the NSA later
described, including signing JSON-RPC messages with asymmetric keys, including nonces and timestamps, and pinning tool definitions using hashes.

OWASP AISVS 1.0, Chapter C10

An entire chapter on MCP Security, with verifiable requirements at L1 to L3. Two requirements map directly to the MCPS spec:

  • 10.2.11 (L2). Agents authenticate using cryptographically bound identity credentials rather than bearer-only tokens, ensuring agent identity cannot be transferred,
    replayed, or impersonated.
  • 10.4.10 (L3). MCP servers sign tool responses with a unique nonce and timestamp within a bounded time window, so the calling agent can verify origin, integrity, and
    freshness, preventing spoofing, tampering, and replay.

CVE-2026-39313, and five more on the clock

CVE-2026-39313. Unbounded-memory-allocation vulnerability in mcp-framework (CWE-770, High), assigned and published 16 April 2026.

Five further CVE submissions, across ~57M weekly downloads of MCP packages (including the official MCP TypeScript and Python SDKs), are under coordinated-disclosure clock.

This is the NSA’s recommendation #4 in action. The package surface is being audited, and the gaps are being closed.

In production, today

moov-io / watchman (Apache 2.0, ~460 stars). Sanctions screening used by SEC-registered transfer agents and BaaS platforms. MCPS and AgentPass are merged into main. The production deployment guide ships an AgentPass configuration block:

AgentPass:
TrustAnchorPath: /etc/watchman/agentpass-ca.pem
MinTrustLevel: 2
RequiredScopes:
– sanctions:search

👉 https://github.com/moov-io/watchman/blob/master/docs/mcp.md

Cisco AI Defense. Cisco’s commercial agent-security product ships our MCPS protocol as part of its agent-defence stack.
👉 https://www.cisco.com/site/us/en/products/security/ai-defense/index.html

Kong API Gateway. A plugin that turns every API behind Kong into an MCPS-signed endpoint with zero developer effort. Available to design partners under NDA.

AEBA-XDR, runtime behaviour analysis for every agent. Anomaly detection in milliseconds. Eight behavioural dimensions, every agent cryptographically identified,
hash-chained tamper-evident audit, native forwarders for major XDR and SIEM platforms via CEF, LEEF and syslog RFC 5424. Free evaluation tier for up to three agents.
👉 https://aeba.co.uk

What to do this week

If you ship MCP in production:

  1. Pin MCP-package versions and subscribe to CVE feeds for every MCP-related dependency. Start with CVE-2026-39313.
  2. Add MCPS message signing to your most-sensitive tool invocations. The spec is small enough to implement from draft-sharif-mcps-secure-mcp in a sprint.
  3. Declare your agent-trust requirements in your OpenAPI document using x-agent-trust. Whether or not your runtime enforces them yet, you have made the requirement
    machine-readable for everything downstream.
  4. Wire your tool invocations into a tamper-evident audit log that maps to the AISVS C10 controls.
  5. Read the NSA notice. If you skim only one document on MCP security this quarter, skim that one.

The standard exists. The reference code exists. The integrations exist. The CVE feed exists.

The protocol can be secured. Now there is no reason not to.

Raza Sharif (FBCS, CISSP, CSSLP)
Founder, CyberSecAI Ltd

Symfony 8 AWS Secrets Bundle

My previous bundle that supported Symfony 5 and 6 was downloaded over 210.000 times, so it looks like it was useful. 😀 I’ve decided to publish an updated one that follows new Symfony bundle architecture as a separate library. More in the second part of this article.

Anyway…

GitHub: https://github.com/constup-foss/symfony8-aws-secrets-bundle

What it does

This bundle allows you to use secrets stored in AWS Secrets Manager as parameter values in your Symfony 8 service container. If you are using AWS Secrets Manager to store encrypted secrets with access control that limits who (or what application) can see and use them through IAM roles, this bundle is for you.

It supports several methods of authentication:

  • by loading a profile from your ~/.aws directory

  • by using long-term credentials (AccessKeyId and SecretAccessKey) from your AWS IAM user

  • by using temporary AWS STS tokens

Using it is very simple and does not require any additional programming from your part. Just set a value of a desired environment variable to the name of your AWS secret, use the provided env var processor to process that environment variable in your service container and you are ready to use the value. That’s it.

The bundle supports using an endpoint to connect to AWS. Useful if your AWS connection is behind your corporate infrastructure or if you want to perform integration tests on something like LocalStack. It also supports fetching individual JSON keys from your AWS secret. Just name the key after the name of the secret when assigning a value to your environment variable.

Complete documentation is available in the repository including:

  • A practical step-by-step example how to use the bundle to load database connection credentials in DoctrineORM;

  • How to use the bundle in different environments (bare metal, docker, AWS CodeBuild pipeline)

A little bit of history and why feedback matters

My original AWS Secrets Bundle (https://github.com/constup-foss/aws-secrets-bundle) was published in August 2021 for Symfony 5. It was just something that I needed for my job at the time and decided to publish it. Apart from just a couple of pull requests, mainly to support Symfony 6 and later 7, there wasn’t much feedback. Barely any GitHub stars (11 in total, mostly from my friends), barely any issues. I honestly thought that no one uses it, and since I’ve mostly moved my work to Node and TypeScript, I just forgot about it.

Fast forward 4 and a half years. I’ve logged in to Packagist to add a new composer library. No one really logs in to Packagist unless they need to publish something. When you do, the statistics are on a page that you don’t need to visit. But, I was in a “cleanup mode”… cleaning up old and obsolete repositories, separating my GitHub account to my personal one and one for FOSS,… so I needed to see if I need to deprecate or delete some libraries.

And the number of was simply there: 212.549 downloads.

I have abandoned the project a long time ago simply because there was no indication that anyone was using it.

So, if you are using a library, consider sending a star. It’s not about vanity, it’s not even about showing gratitude. It’s about letting library maintainers know that their work is used so that they don’t abandon it thinking that no one does… just like I did.

A really simple solution made by Symfony

Symfony made a library to help with this: https://github.com/symfony/thanks

Require it in your projects as a dev dependency and do composer thanks. It will send stars to libraries that your project is using.

Anyway… That’s it for now. I hope my bundle serves you well. And don’t forget to star it 😉

What RepoSignal Surfaced in React — and Why Review Alone Doesn’t Catch Everything

What RepoSignal Surfaced in React — and Why Review Alone Doesn’t Catch Everything

  • · RepoSignal.io · May 2026 *

The React repository has over 220,000 GitHub stars. It is maintained by Facebook engineers, reviewed by thousands of contributors, and used by millions of developers worldwide. It is one of the most scrutinized open source codebases in existence.

We connected it to RepoSignal.

Within 24 seconds, the scanner returned 20 findings requiring review: 16 high severity and 4 medium severity. Each finding maps to a real code location and a real pattern. Exploitability depends on execution context — that is always true of static analysis output. But every one of these findings points to something a reviewer should examine.

Scan metadata: Branch: main · Date: May 22, 2026 · Scanner: RepoSignal v3

Note: React is an actively maintained repository. Some patterns may have been addressed in subsequent commits. The commit hash pins the exact state scanned.

What RepoSignal Surfaced

Severity Pattern Location
HIGH Dynamic code execution (eval/exec) compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts
HIGH Dynamic code execution (eval/exec) compiler/packages/react-compiler-healthcheck/src/checks/libraryCompat.ts
HIGH Dynamic code execution (eval/exec) compiler/packages/react-compiler-healthcheck/src/checks/reactCompiler.ts
HIGH Dynamic code execution (eval/exec) compiler/packages/react-compiler-healthcheck/src/checks/strictMode.ts (×4)
HIGH Dynamic code execution (eval/exec) compiler/scripts/release/shared/utils.js
HIGH Dynamic code execution (eval/exec) packages/react-client/src/ReactFlightReplyClient.js
HIGH Dynamic code execution (eval/exec) packages/react-devtools-inline/playwright.config.js
HIGH Dynamic code execution (eval/exec) packages/react-devtools-shared/src/backend/utils/parseStackTrace.js
HIGH Dynamic code execution (eval/exec) packages/react-devtools-shared/src/backend/utils/views/utils.js
HIGH Dynamic code execution (eval/exec) packages/react-devtools-shared/src/devtools/utils.js
HIGH Dynamic code execution (eval/exec) packages/react-devtools-shared/src/devtools/views/Components/IndexableDisplayName.js
MEDIUM Debug output may leak credentials packages/react-devtools-shared/src/backend/fiber/renderer.js
MEDIUM Debug output may leak credentials packages/react-devtools-shared/src/backend/legacy/renderer.js
MEDIUM Trust boundary violation packages/react-devtools-shared/src/devtools/views/utils.js (×2)

Why RepoSignal Surfaces These Patterns in React

This is the first question anyone should ask, and it deserves a careful answer.

React is a heavily reviewed project. The Facebook engineering team runs security audits. Thousands of contributors read the code. And yet 20 patterns came back in 24 seconds.

This is not an indictment of React. It is a demonstration of something more important: static analysis and human code review are looking for different things.

Human reviewers are optimized for architecture, logic, API design, and performance. They are not optimized to systematically enumerate every eval() call across 400,000 lines of code spanning a dozen packages at every commit. Automated scanners are.

Most of the eval() instances in React appear in compiler tooling and devtools — infrastructure that runs in development environments, not production user-facing runtime. Many are likely intentional or controlled. The scanner correctly surfaces them as patterns that warrant examination, because eval() with untrusted input is a well-documented path to arbitrary code execution. The scanner surfaces the pattern; a human makes the exploitability call.

Static findings alone do not predict breakage. RepoSignal combines findings with repository-specific historical behavior because many corrected PRs contain no obvious security pattern at all — they break things for reasons that only become visible by looking at what similar changes to the same codebase have done before.

This division of labor — scanner covering pattern space, reviewer covering context, RepoSignal covering empirical history — is how systematic code review at scale works.

What Scanners See That Reviewers Miss

Static scanners and human reviewers are not competing — they cover different ground.

Static Scanner Human Reviewer
Systematically covers entire codebase ✓ ✗
Catches architectural logic errors ✗ ✓
Flags known dangerous patterns ✓ Inconsistently
Understands business context ✗ ✓
Scales with commit velocity ✓ ✗
Systematically predicts correction likelihood ✗ ✗

That last row is where RepoSignal adds a layer that neither scanners nor reviewers provide: empirical PR risk scoring based on the repository’s own correction history.

The Scale of the Pattern

Dynamic code execution via eval() is one of the most documented security risks in JavaScript and TypeScript development. The OWASP Top 10 has consistently included injection attacks — the class of vulnerability that uncontrolled dynamic execution enables — as a top risk category for over a decade.

The credential exposure context is also worth understanding. GitGuardian’s 2024 State of Secrets Sprawl report found that 12.8 million new secrets were exposed in public GitHub repositories in 2023 — a 28% increase over the prior year.1 By 2024 that number reached 23.8 million, based on analysis of 1.4 billion commits.2 More striking: 70% of secrets leaked in 2022 were still active two years later.3

A 2024 peer-reviewed study published in ACM Proceedings on Software Engineering found that neural code completion tools can memorize and re-emit hard-coded credentials from their training data.4 As AI-assisted development accelerates, the credential exposure risk compounds across any codebase. GitGuardian’s 2026 analysis found that AI-assisted commits expose secrets at roughly twice the baseline rate.5

Dynamic execution patterns like eval() carry a parallel risk profile. JavaScript’s eval() function executes arbitrary code from a string — if that string includes user-supplied input, it becomes a direct injection vector. The pattern is well-documented: OWASP’s 2021 injection category covers it directly. Developer tooling and compiler infrastructure deserve particular review because they often process external input during build and development workflows while receiving less production-focused scrutiny than runtime code. The React scan found 16 high-severity instances of this pattern concentrated in compiler and devtools infrastructure — packages involved in compiler and development workflows.

The Cost Argument for Earlier Detection

One of the most durable findings in software engineering research is that the cost to fix a vulnerability grows exponentially as it moves through the development lifecycle. IBM’s Systems Sciences Institute research quantifies this: fixing a defect during implementation costs six times more than catching it during design.6 For vulnerabilities that reach production, the multiplier is 30x or higher by some estimates.7

The global average cost of a data breach in 2024 was $4.88 million.8

The practical argument for scanning at the PR level is not theoretical. Every pattern flagged on a pull request is a pattern that does not become a production incident, an emergency patch cycle, or a breach notification.

The React patterns RepoSignal surfaced are not production incidents. They are review signals surfaced at the point in the workflow where they are cheapest to address.

What RepoSignal Would Do on a React Pull Request

The scan above covers the repository at HEAD — a snapshot of what exists. The more powerful use of RepoSignal happens at the pull request level.

Imagine a contributor opens a PR that adds another dynamic execution path to the compiler. Here is what happens:

  1. The scanner flags the pattern — same as the scan above, but scoped to the diff.
  2. The repository model checks historical behavior — RepoSignal has learned from React’s own commit and revert history. It knows which types of changes to the compiler have historically required correction.
  3. The PR gets prioritized for senior review — not blocked automatically, but surfaced with context. For example, a message might look like: “Similar changes to this area were corrected 2.83× more often than baseline.”
  4. The reviewer sees findings with priority guidance — RepoSignal’s KStar prioritization engine groups findings by shared architectural pattern. Multiple eval() detections in the same package are treated as one repeated structural concern rather than independent failures, reducing alert noise.

This is the combination that distinguishes RepoSignal from a standalone scanner: static pattern detection plus empirical PR prioritization based on the repository’s own history.

How RepoSignal Prioritizes Findings

RepoSignal does not return a flat list of 20 findings. Its KStar prioritization engine groups findings by shared architectural pattern to reduce repeated alerts and surface what actually needs attention.

For the React scan, KStar grouped the findings into a shared architectural pattern centered on dynamic execution and tooling exposure. The practical guidance this produces:

These findings share a common architectural pattern. Start with the highest severity finding.

For React, that means starting with the highest-severity eval() instances in the compiler packages — specifically those in Entrypoint/Program.ts and the healthcheck utilities, where dynamic execution patterns concentrate in compiler infrastructure.

What This Means for Your Repository

facebook/react is not a cautionary tale about React’s security posture. It is a demonstration of what systematic pattern detection surfaces in any sufficiently large codebase — including yours.

A team shipping 5-10 pull requests a day is merging code that no single reviewer reads end to end. Static analysis does not replace review. It ensures systematic coverage of the pattern space that human attention cannot maintain across an entire codebase at velocity.

Connect a repository and see what patterns exist today — and which pull requests history says deserve attention next.

Get started free → reposignal.io

References

RepoSignal combines repository learning, PR risk scoring, and static analysis so teams know both what patterns exist in their code today and which pull requests history suggests deserve extra review tomorrow. Free tier available at reposignal.io.

  1. GitGuardian, State of Secrets Sprawl 2024, March 2024. https://www.gitguardian.com/state-of-secrets-sprawl-report-2024 ↩

  2. MSSP Alert, More than 23.7 Million Hardcoded Secrets Publicly Exposed in GitHub Last Year, March 2025. https://www.msspalert.com/brief/more-than-23-7-million-hardcoded-secrets-publicly-exposed-in-github-last-year ↩

  3. ZeroHost Security Blog, 23.8 Million Secrets Leaked on GitHub: The Case for Credentials That Expire, February 2026. https://zerohost.net/blog/github-secrets-leaked-case-for-expiring-credentials/ ↩

  4. Huang et al., Your Code Secret Belongs to Me: Neural Code Completion Tools Can Memorize Hard-Coded Credentials, ACM Proceedings on Software Engineering, FSE 2024. https://arxiv.org/pdf/2309.07639 ↩

  5. Snyk, Why 28 Million Credentials Leaked on GitHub in 2025, and What to Do About It, March 2026. https://snyk.io/articles/state-of-secrets/ ↩

  6. Black Duck / Synopsys, Cost to Fix Bugs and Defects During Each Phase of the SDLC, citing IBM Systems Sciences Institute. https://www.blackduck.com/blog/cost-to-fix-bugs-during-each-sdlc-phase.html ↩

  7. Cypress Data Defense, Finding Application Vulnerabilities Early: Why It Matters, March 2025. https://www.cypressdatadefense.com/blog/finding-application-vulnerabilities-early-why-it-matters-and-how-to-succeed-with-limited-resources/ ↩

  8. IBM, Cost of a Data Breach Report 2024, cited in Security Compass, The High Costs of Delaying a Security by Design Program, September 2024. https://www.securitycompass.com/blog/the-high-costs-of-delaying-a-security-by-design-program/ ↩