Sovereign Synapse: The Great Export

A diagram showing the flow of raw JSON data from a cloud service being transformed into structured Markdown files in a local vault.<br>

For years, we have treated LLMs as a rented brain. We have poured our debugging sessions, research threads, and early project drafts into cloud-hosted chat windows, treating them as convenient extensions of our own thinking.

But, data you do not own is an Infrastructure Tax you cannot afford to pay forever.

This post kicks off a new build thread: Sovereign Synapse. We are initiating a digital evacuation—pulling our intellectual history out of the cloud and into a local, human-readable vault.

Builder’s Note: The Fiscal Architecture of Data
After recent discussions, it’s clear that “Sovereign AI” starts at the ingestion layer. In production, “Privacy” is actually a Financial Strategy. By moving our intellectual assets to local silicon, we eliminate the “Prose Tax”—the expensive tokens wasted on cloud system prompts trying to explain raw, messy data to an agent. We aren’t just saving files; we are building a Sovereign Gateway that ensures every dollar spent on cloud inference is spent on execution, not on interpretation.

The Problem: The Fragmented Self
Your intellectual assets are currently scattered across Claude, ChatGPT, and Gemini. As long as these thoughts live on a corporate server, they are subject to shifting terms of use and “Service Discontinued” notices.

For those using these tools to document a lifetime of expertise, this fragmentation is a risk to Data Provenance. We need a Cognitive Estate that stays on our own silicon, ensuring our reasoning is stored as a Structural Contract, not a digital attic.

The Architecture: The Forensic Ingestor

To reclaim this data, we don’t want a disorganized data dump. We want a Synapse. Our first tool is a Forensic Ingestor that transforms raw, nested JSON exports into atomic, “Turn-Based” Markdown files.

A diagram showing the flow of raw JSON data from a cloud service being transformed into structured Markdown files in a local vault.<br>
The Digital Evacuation: Moving from cloud-hosted ‘rented’ thoughts to a locally-owned Cognitive Estate.

The Build: The Sovereign Adapter

We focus on Deterministic ID generation to ensure our Forensic Trace remains unbroken. By hashing the user intent with a timestamp, we create a Forensic Receipt that anchors this memory forever, allowing us to map causal chains across different sessions later.

# adapters/synapse_adapter.py 
import hashlib
import json

def generate_typed_asset(user_text, timestamp, category="Technical/Logic"):
    """
    Transforms a 'Text Blob' into a 'Sovereign Asset.'
    By typing the reasoning during ingestion, we eliminate the 
    'Prose Tax'—the expensive tokens wasted on system prompts 
    trying to explain raw data to an agent.
    """
    # Create a deterministic anchor for the Forensic Trace
    seed = f"{user_text[:100]}-{timestamp}"
    asset_id = hashlib.sha256(seed.encode()).hexdigest()

    return {
        "asset_id": asset_id,
        "type": category,
        "schema_version": "1.0",
        "is_audit_ready": True
    }

# Logic for traversing OpenAI's conversation tree and 
# extracting the "Turn" goes here...

First Light: The Mobility Audit

When I ran this against my own data, the first “Synapse” to appear in my vault was a 2024 conversation about raw data wearables for mobility tracking.

In a medical setting, tracking gait and balance is a critical marker for neurological health. By capturing this conversation locally, I’ve preserved a specific piece of reasoning regarding the Movesense Medical Sensor and MetaMotion R hardware. That conversation is now a Verified Asset. It is no longer a ‘chat history’; it is a queryable part of my own intellectual history—ready for the Sovereign Network.

What is the one conversation in your history that you can’t afford to lose?

The Sovereign Synapse Series

  • The Great Export – This Post
  • The Context Cleaner – Coming 26 May 2026
  • The Local Brain – Coming 2 June 2026
  • The View from the Summit – Coming 9 June 2026
  • The Synapse Navigator – Coming 16 June 2026
  • The Analog Bridge – Coming 23 June 2026
  • The Temporal Mirror – Coming 30 June 2026
  • The Unbroken Voice – Coming 7 July 2026

SeekYou: How I Built a Unified Host Intelligence Tool That Runs Free on Cloudflare Workers

One input. 15 sources. One structured report. Zero infrastructure costs.

The Problem

Every security engineer knows the workflow. You get an IP, a domain, or an ASN you need to investigate. You open Shodan in one tab, BGPView in another, crt.sh in a third. You paste the same value into VirusTotal, then into a passive DNS provider, then into two or three threat feeds. Twenty minutes later you have seven browser tabs, a notes file full of copy-pasted snippets, and you’re manually trying to reconcile whether the cert history matches the passive DNS timeline.

It’s not a hard problem. It’s just a tedious one that nobody had solved cleanly for the open-source side of the house.

So I built SeekYou.

What SeekYou Does

SeekYou takes any IP, domain, or ASN as input and returns a single structured intelligence report by querying 15 sources in parallel:

  • Open ports and service banners
  • Known CVEs mapped to running services
  • BGP and routing data
  • RDAP / WHOIS enrichment
  • Certificate history via crt.sh
  • Passive DNS records
  • 5 threat feed lookups — malicious classification, abuse scoring, blocklist status
  • Exposed cloud buckets
  • Wayback Machine snapshots — useful for infrastructure history and phishing page archaeology

The key word is parallel. All 15 sources are queried simultaneously, so your total response time is roughly equal to the slowest single source — not the sum of all 15.

Architecture: Why Cloudflare Workers

I had a few hard requirements going in:

  1. No self-hosted infrastructure — I wanted this to run for free and be instantly deployable by anyone
  2. True parallel execution across 15 external sources
  3. Caching granular enough to be actually useful
  4. Rate limiting and circuit breaking without an external service

Cloudflare Workers satisfied all four cleanly.

The V8 Isolate Advantage

On traditional serverless platforms, cold starts can add hundreds of milliseconds per invocation. When you’re fanning out to 15 sources simultaneously, that compounds fast. Workers’ V8 isolate model eliminates that problem — isolates start in microseconds, making the fan-out pattern genuinely viable.

4-Layer Parallel Execution Model

Layer 1 — Input parsing and normalization
Layer 2 — Source fan-out (all 15 queries fire simultaneously)
Layer 3 — Per-source response parsing and normalization
Layer 4 — Report assembly and diff comparison

Each layer is non-blocking. Layers 3 and 4 begin processing as soon as individual sources return — you’re not waiting for all 15 to complete before starting assembly.

Per-Source KV Caching

Each source response is cached independently with a TTL tuned to that source’s data freshness:

const SOURCE_TTLS = {
  ports: 3600,
  certs: 86400,
  threatFeeds: 1800,
  bgp: 43200,
  passiveDns: 7200,
};

Partial cache hits work. If 12 of 15 sources are cached, only 3 cold fetches happen. Per-source caching gives proportional speedups on repeat queries — critical for monitoring workflows.

Circuit Breakers

Each source is wrapped in a circuit breaker implemented directly in the Worker:

async function fetchWithCircuitBreaker(source, fetcher) {
  const state = await kv.get(`circuit:${source}`);

  if (state === 'open') {
    return { source, status: 'circuit_open', data: null };
  }

  try {
    const result = await fetcher();
    await kv.put(`circuit:${source}`, 'closed');
    return { source, status: 'ok', data: result };
  } catch (err) {
    await incrementFailureCount(source);
    return { source, status: 'error', data: null };
  }
}

A source failing doesn’t kill the report — it returns a graceful null and the rest assembles normally.

Per-IP Rate Limiting

Rate limiting is handled natively in the Worker using KV as the backing store. No external Redis, no rate limiting service. Each client IP gets a sliding window counter, and requests over the threshold get a clean 429 before touching any upstream source.

The Diff Engine

SeekYou can monitor a host over time and alert you when its observable state changes. The diff engine is typed — it tells you specifically what category of change occurred:

  • A port opened or closed
  • A new CVE appeared against a running service
  • A certificate rotated or is approaching expiry
  • The host appeared on a new threat feed
  • A new subdomain appeared in passive DNS
{
  host: "example.com",
  timestamp: "2026-05-19T10:00:00Z",
  changes: [
    { type: "port_opened", detail: { port: 8443, service: "HTTPS" } },
    { type: "cve_added", detail: { id: "CVE-2024-XXXX", severity: "HIGH" } },
    { type: "cert_expiry_warning", detail: { daysRemaining: 14 } }
  ]
}

This turns SeekYou from a one-off lookup tool into something you can build alerting workflows around.

Free Tier Math

Resource Free Limit Usage at 5k lookups/day
Worker requests 100,000/day ~80,000 (orchestration + source fetches)
KV reads 100,000/day Proportional to cache hit rate
KV writes 1,000/day One write per cache miss per source
CPU time 10ms/request Well within limit

At 5k full lookups per day you’re comfortably within the free tier on every dimension.

Deployment

git clone https://github.com/Teycir/SeekYou
cd SeekYou
npm install
wrangler deploy

Cloudflare account and Wrangler CLI required. No paid plan needed.

What I’d Build Next

  • Webhook support for the diff engine — push changes to Slack, Discord, or a SIEM
  • Bulk input — accept a list of IOCs via Workers Cron Triggers
  • STIX export format for formal TI workflows
  • Additional sources — Censys, Fofa, and more threat feeds

Wrapping Up

The core insight behind SeekYou is simple: the data is publicly available, the sources all have free tiers, and the only missing piece was an orchestration layer that queries them simultaneously and returns one coherent report. Cloudflare Workers was the right substrate — the isolate model, KV, and native rate limiting covered everything without adding infrastructure complexity.

GitHub: https://github.com/Teycir/SeekYou

All-in-one Docker Server Dashboard

Hello!
I am making an all-in-one server dashboard for Linux servers using docker. I am creating it using Node.js, PostgreSQL, and my own backend (API).

This dashboard is supposed to be a dashboard where developers can pick their own login method, database, and also deploy their own apps easily with a click of a button. I want to create a VSCode extension, CLI, and my own templates so that it’s easier for developers to connect their server and code all together. This will also have a shortcut feature to easily open their apps from the Dashboard. I was originally creating this for developers to use services as a way to deploy their own websites and projects.

Does anyone have any suggestions that would be good for this server dashboard?

A closer look at partial prerendering

network tab streaming

Partial prerendering replaces the old binary model of rendering. Before, there were 2 cases. Either the entire route was statically rendered at build time or – if any component in the route had a dynamic element – the entire route was dynamically rendered, that is, rendered server-side at request time.

Dynamic rendering is triggered automatically when a component uses:

  • headers, cookies or connection function
  • searchParams or params page prop
  • draftMode
  • dynamic fetch (fetch with no cache)

The downside to dynamic rendering is obvious. Components that could be statically rendered aren’t because the route contains a dynamic element. This is exactly what PPR solves. When a route is made up of static and dynamic components, PPR will statically render the static components into a static shell for the route. The dynamic components must be wrapped inside suspense. The suspense fallback is included into the static shell and the dynamic component itself will be streamed in at runtime.

Example

Note: the examples are available on a new repo on github.

First off, we need to turn on PPR. This is simple, add following config option:

// next.config.ts

const nextConfig: NextConfig = {
  cacheComponents: true,
};

We will create some components to form our static shell:

// components/global/Header.tsx

export default function Header() {
  return <header>**** Header ****</header>;
}

// components/global/Footer.tsx

export default function Footer() {
  return <footer>**** Footer ****</footer>;
}

Then we create a dynamic component. We use connection to keep it simple.

// components/ppr/SimpleConnection.tsx

export default async function SimpleConnection() {
  await connection();
  return <div>Hello world!</div>;
}

Finally, we put it all together in a route:

// ❌ this example fails, see below
// app/chapter-12/connection/page.tsx

export default function Page() {
  return (
    <>
      <Header />
      <SimpleConnection />
      <Footer />
    </>
  );
}

This is not how you should do it. We needed to wrap <SimpleConnection /> inside suspense. But I wanted to demonstrate it first without. When we run next build, we get an error and the build fails:

Error: Route "/chapter-12/connection": Uncached data was accessed outside of <Suspense>. This delays the entire page from rendering, resulting in a slow user experience.

...

This is great; when we forget suspense, Next shouts at us! We update the example with suspense:

export default function Page() {
  return (
    <>
      <Header />
      <Suspense fallback='...loading'>
        <SimpleConnection />
      </Suspense>
      <Footer />
    </>
  );
}

This is pure and simple PPR. Given what we learned we expect 2 things:

  1. At build time: A static shell with header, footer and the loading fallback.
  2. At runtime: the fallback gets swapped with the resolved component.

1. We expect a static shell

We run Next build. The build log gives us the first information:

Route (app)
├ ◐ /chapter-12/connection

○  (Static)             prerendered as static content
◐  (Partial Prerender)  prerendered as static HTML with dynamic server-streamed content

We have partial prerendering.

Let’s take a look at the static shell now. We know from earlier chapters that Next will generate HTML and rsc at build time. This is also true for PPR. On top of that the build folder is the same. So, we can take a look at it. Here is a cleaned-up version of the prerendered HTML:

<!-- .nextserverappchapter-12connection.html -->

<!doctype html>
<html lang="en">
  <body>
    <header>**** Header ****</header>
    <!--$?--><template id="B:0"></template>...loading<!--/$-->
    <footer>**** Footer ****</footer>
  </body>
</html>

As for the rsc, that’s a bit different. In a previous chapter, we looked at the rsc payload of static rendering. We learned that each route had an HTML and rsc file on the same level. So, for example app/example/page.tsx would be generated as example.html and example.rsc in the build folder .next/server/app.

PPR routes are a bit different. The rsc sits in a subfolder and is divided across multiple files, segments. But that doesn’t really matter. Here is a cleaned-up rsc snippet:

// .nextserverappchapter-12connection.segments_full.segment.rsc
[
  ['$', 'header', null, { children: '**** Header ****' }],
  ['$', '$5', null, { fallback: '...loading', children: '$L6' }],
  ['$', 'footer', null, { children: '**** Footer ****' }],
];

Let’s take a small step back. We’re looking at a route that would’ve rendered dynamically in the previous model because it contains a dynamic element: connection. But we’re now prerendering it partially. Everything in the route that can be statically rendered is prerendered. This is the static shell.

The dynamic parts are skipped. We prerender a placeholder (fallback) into the static shell and when a request is made at runtime, the dynamic component is resolved server side in the background and then streamed to the client where it simply is swapped with the placeholder.

We just looked at these static shells. HTML for initial requests and rsc for client side routing. We saw the static parts: header and footer and the fallback: “…loading”.

2. We expect streaming

The dynamic elements are not prerendered. To render them, we need to run the app. We want to render <SimpleConnection />:

// components/ppr/SimpleConnection.tsx

export default async function SimpleConnection() {
  await connection();
  return <div>Hello world!</div>;
}

We already ran next build. Now we run next start to run the app and visit our route: /chapter-12/connection. We can see the loading state very brief and then it gets swapped with “Hello world!”. So it works.

Everything happens server side at request time. Nothing is saved or cached or put into the build folder so we can’t take a look at it.

What is streamed?

We took a deep look at how static and dynamic rendering works in earlier chapters and we learned that routes are rendered into HTML and rsc files. The HTML and rsc get served on initial load. On client side routing however, only the rsc is used to update routes. We know this. Streaming works similarly.

On initial load, HTML get streamed in. On client side routing, rsc is streamed. We can use our /chapter-12/connection example and look at the network tab in our dev tools to see this. When we’re on the route /chapter-12/connection and refresh the tab (simulate on first load) we see something interesting in our network tab. We’re looking at the root document file (the html file)

network tab streaming

When we inspect this file we recognize the prerendered static shell from earlier but also something else:

<!-- Response  -->
<!-- edited: cleaned up -->

<!DOCTYPE html>
<html lang="en">
  <body>
    <!--&-->
    <header>**** Header ****</header>
    <!--$?-->
    <template id="B:0"></template>
    ...loading
    <!--/$-->
    <footer>**** Footer ****</footer>
    <!--/&-->
    <!-- scripts here -->
    <div hidden id="S:0">
      <div>Hello world!</div>
    </div>
    <!-- scripts here -->
  </body>
</html>

This part:

<div hidden id="S:0">
  <div>Hello world!</div>
</div>

That is our resolved and generated <SimpleConnection /> component, inside our static shell. This was streamed in. Next kept the connection open and … added this. I have no idea how these connections work so I can’t explain the how. But it’s the what that is important here. Extra HTML was streamed into the HTML file that Next first sent to the browser.

It is a two part process. First, the static shell that was prerendered at build time was send to the browser. Then, when Next rendered the dynamic component, it sent over some more HTML into the same file. The fallback swap is easy then. Take content of div with ID X and put it into div with ID Y.

So, on initial load, Next renders dynamic elements into HTML and streams it to the browser.

On client side navigation, only rsc is used. I added some links in my examples so we can navigate to our route. We clear our network tab and navigate to route /chapter-12/connection. A single rsc files appears (well, 2 icons as well but meh): http://localhost:3000/chapter-12/connection?_rsc=ob2DjmuOZafxCs0z.

Streaming rsc

As expected, it’s rsc and it’s complex. But, we can just about make out the original static shell fragments and also content of the resolved component (“Hello world!”). How Next uses this rsc to paint in the browser doesn’t matter. We only wanted to confirm rsc was streamed.

Client components

Maybe you’re wondering how client components fit into the PPR model. They don’t. PPR does not affect client components.

Quick recap: client components are components that use:

  • hooks or custom hooks
  • interactive element (event listeners)
  • browser only APIs (e.g. localStorage)

Dynamic components are components that use dynamic functions (headers, cookies, searchParams or params page prop, connection or draftmode) of dynamic fetches (no caching).

Dynamic components and client components don’t overlap. However, all dynamic functions are asynchronous while client components cannot be async. This means that client components cannot be dynamic. All of this isn’t new.

Client components never require a dynamic suspense boundary – because they can’t be dynamic. Except when using the useSearchParams hook. But we covered this already in a previous chapter.

Client components can of course be inside suspense boundaries as a child of a dynamic component. Take a look at this example:

// app/chapter-12/dynamic-with-client-child/page.tsx

export default function Page() {
  return (
    <>
      <Header />
      <Suspense fallback='...loading'>
        <DynamicParent />
      </Suspense>
      <Footer />
    </>
  );
}
// components/ppr/DynamicParent.tsx

export default async function DynamicParent() {
  await connection();
  return (
    <div>
      <h1>**** Dynamic component</h1>
      <ClientChild />
    </div>
  );
}
// components/ppr/ClientChild.tsx

'use client';

import { useState } from 'react';

export default function ClientChild() {
  const [count, setCount] = useState(0);
  return (
    <>
      <div>**** count: {count}</div>
      <button onClick={() => setCount((prev) => prev + 1)}>add one</button>
    </>
  );
}

So, we have suspense that wraps a dynamic component and said component imports a client component.

Just before, we learned that dynamic components are streamed as HTML on initial load or as rsc on client side routing. But what about our client component?

On initial load, the client component will also be rendered server-side into a non interactive shell, just the HTML, no event listeners:

<div>**** count: {count}</div>
<button>add one</button>

This is streamed to the client, as HTML to the HTML document file, where it is swapped with the fallback. Once the entire client bundle is loaded, Next will virtually run <ClientChild /> again in the browser and use it to hydrate the prerendered HTML.

On client side routing, the process is different. Only rsc is streamed. The server resolves <DynamicParent /> and renders it into rsc. Inside this rsc, there will be a placeholder that refers to <ClientChild />. <ClientChild /> does not get rendered into rsc itself. The rsc is sent to the client. On the client, <ClientChild /> is rendered and fills the placeholder. Finally, this rendered chunk (<DynamicParent /> and <ClientChild />) is swapped with the fallback. Hydration of <ClientChild /> is not needed since there was no prerendered HTML.

This is just normal client component rendering behaviour. We already covered this in a previous chapter. As I said before, PPR does not affect client components. The only difference is the suspense boundary.

Managing boundaries

In the old model, once you used a dynamic element, the route became dynamic and that was that. Nothing got prerendered. In the PPR model, boundaries determine dynamic rendering and these boundaries need to be managed.

Every dynamic element needs to be resolved: headers, cookies, searchParams page prop, connection and dynamic fetches are all async functions. They return a promise that needs to be resolved. To resolve a promise in PPR, you need to wrap it in suspense (or use loading.ts). Elements wrapped in suspense do not get prerendered. But we want to prerender as much as possible. Therefore, we need to be careful with using suspense.

The first way to optimize this is to place suspense boundaries as far down a component tree as possible. Take a header for example with a logo, a menu and a user account icon. Make the user account icon a separate component and wrap it in suspense. The other elements in the header can then be statically prerendered. We always want to maximize static rendering.

This isn’t always obvious. Here is an example. We use our previous example but rework it with the searchParams page prop instead of connection:

// app/chapter-12/searchparams/page.tsx
// ❌ this example fails, see below

export default async function Page({
  searchParams,
}: PageProps<'/chapter-12/searchparams'>) {
  const { name } = await searchParams;
  return (
    <>
      <Header />
      <Suspense fallback='...loading'>
        <SimpleSearchParams name={name} />
      </Suspense>
      <Footer />
    </>
  );
}
// components/ppr/SimpleSearchParams.tsx

type Props = {
  name: string | string[] | undefined;
};

export default function SimpleSearchParams({ name }: Props) {
  const validatedName = !name ? 'guest' : Array.isArray(name) ? name[0] : name;
  return <div>Hello {validatedName}</div>;
}

This fails at build time. Why? Because we resolved searchParams inside our page component. When Next encounters a promise that needs to be resolved, it marks the component as dynamic and looks for a suspense boundary and we don’t have one. This is the error:

Error: Route "/chapter-12/searchparams": Uncached data was accessed outside of <Suspense>.

A very bad ❌ way to solve this problem would be to add a loading.tsx file in our page route. The error will be gone, but the entire page will be rendered dynamically, including our <Header /> and <Footer /> components. This is exactly what we don’t want.

The correct ✅ way to solve this is by passing the promise – unresolved – to our <SimpleSearchParams /> component:

export default function Page({
  searchParams,
}: PageProps<'/chapter-12/searchparams'>) {
  return (
    <>
      <Header />
      <Suspense fallback='...loading'>
        <SimpleSearchParams searchParams={searchParams} />
      </Suspense>
      <Footer />
    </>
  );
}

The component is no longer async and we just pass the searchParams promise. We then resolve this promise in our <SimpleSearchParams /> component:

type Props = {
  searchParams: Promise<Record<string, string | string[] | undefined>>;
};

export default async function SimpleSearchParams({ searchParams }: Props) {
  const { name } = await searchParams;
  const validatedName = !name ? 'guest' : Array.isArray(name) ? name[0] : name;
  return <div>Hello {validatedName}</div>;
}

We run build and everything runs fine, we have a PPR route with maximum static rendering. Passing a promise down is a second tool you can use to maximize static rendering.

Migrating terminology

With PPR, Next changed some terminology. Here are two of those changes:

The terms dynamic elements or dynamic fetches are no longer used. Instead these are now categorized as:

  • Streaming uncached data (instead of dynamic fetches).
  • Runtime APIs: headers, cookies, params and searchParams page props (instead of dynamic elements).
  • Non-deterministic operations (Math.random(), Date.now(), crypto.randomUUID()) that require connection (instead of dynamic elements).

So, the enveloping term dynamic elements is out of use. I’m not sure this is a good change. It needs to be clear what causes a component to become dynamically rendered and that seems less clear with this change.

Full route cache is no longer mentioned. What used to be the full route cache is now only referred to as the static shells. Again, not a fan of this change. Having a term like full route cache made me understand caching in Next better.

Static, dynamic and partial prerendering

For now we skipped caching and data fetching. We will cover this in later chapters. But, I want to make it clear in this chapter that static rendering still exists in the PPR model. Static rendering, a 100% prerendered route with no dynamic elements or suspense boundaries is still possible. PPR does not eliminate this.

Dynamic rendering has become mostly obsolete by PPR. In most cases we do not want to dynamically render an entire route. We want to prerender as much as possible. That is what PPR allows us to do. However, should you need it, it is still possible to dynamically render a route. Place a suspense with fallback null in the root layout.tsx:

// app/layout.tsx

import { Suspense } from 'react';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <Suspense fallback={null}>
        <body>{children}</body>
      </Suspense>
    </html>
  );
}

When Next encounters null as fallback in the root layout, it will not generate a static shell. This means that this route will have to be generated server side at request time = dynamic rendering. Note that this setup will make every route dynamic. You can alter this by using multiple root layouts. Finally, note that these routes will again be blocking. Nothing will be sent over to the client before everything is resolved and generated.

loading.tsx

We’ve mostly been using suspense. Note that you can also use the loading.tsx to add a suspense boundary in PPR. See the docs for more info.

Conclusion

I hope this article gave you a more grounded feel for partial prerendering.

In the next chapters we will cover caching with cache components.

If you want to support my writing, you can donate with paypal.

Building System: A Modern Multi-Vendor Book Marketplace in Kurdistan

Hello connections! As a Co-founder and Tech Lead, I’m thrilled to share some technical insights into building Pertwk.krd, Kurdistan’s upcoming multi-vendor book marketplace.
To ensure high scalability, maximum performance, and a seamless user experience, we engineered the platform from the ground up using a modern, API-first architecture:
Backend: Powered by Laravel to handle robust APIs, advanced security, and dynamic subdomain management for our bookstore vendors.
Frontend: Built with Nuxt.js / Vue.js and Tailwind CSS to deliver a minimalist, lightning-fast, and responsive UI/UX.

Right now, my primary focus is optimizing the platform for our most exciting milestone yet: processing our very first order! 
If you’re interested in e-commerce architecture, SaaS development, or want to connect for technical discussions, feel free to visit my personal website: https://shadarf.dev

Laravel #NuxtJS #VueJS #EcommerceDevelopment #TechLead #SoftwareDeveloper #WebDevelopment #Kurdistan #Pertwk #shadarf

OpenCode vs Claude Code: Real Threat or Just Another Niche Alternative?

OpenCode vs Claude Code — Header

OpenCode vs Claude Code — Header

If you use terminal coding agents, you know Claude Code is the king. But over the past few months, a competitor has emerged with staggering numbers: 160,000 GitHub stars, 900 contributors, 13,000 commits, and a direct proposition: “the same as Claude Code, but open source, multi-model, and no monthly fee.”

It’s called OpenCode. And the question every developer is asking: does it actually compete with Claude Code, or is it just a polished TUI?

I tested both and read everything published by those who tested them seriously. This article isn’t a benchmark — it’s an honest comparison, with each tool’s wins and losses laid bare.

What Each One Is

OpenCode is an open-source AI coding agent built in Go that runs in the terminal, desktop, and IDE. Install it in under a minute with curl -fsSL https://opencode.ai/install | bash. Its thesis: you bring the model, OpenCode brings the agent. It supports 75+ LLM providers — from Claude and GPT to local models via Ollama. It doesn’t store your code, doesn’t charge a subscription, and over 7.5 million developers use it monthly.

Claude Code is Anthropic’s official agent, launched in February 2025. It runs exclusively Claude models (Opus 4.5, Sonnet 4.5, Haiku 4.5). It requires a subscription ($20/month Pro, $100/month Max). It’s the industry benchmark: the first to break 80% on SWE-bench Verified, the agent teams like Doctolib use to accelerate deploys by 40%.

Key differences at a glance:

OpenCode Claude Code
License MIT (open source) Proprietary
GitHub Stars 161K 124K
Models 75+ (Claude, GPT, Gemini, Ollama…) Anthropic only
Tool Cost $0 $20-200/month
Interface Multi-pane TUI Terminal chat
Vim Bindings No Yes
Visible Subagents Yes No (background)
Context Sidebar Yes (live token count) No
GitHub Actions Native (/opencode) Headless mode
AGENTS.md Supported Not read

Where Claude Code Is Still King

Pure Agent Performance

When comparing both using the same model (Claude Sonnet 4.5), Claude Code wins by 8 percentage points on complex tasks. In independent tests across 38 tasks — multi-file refactors, bug fixes, new features, test generation — Claude Code completed 82% vs OpenCode’s 74%.

The difference isn’t the model. It’s how Anthropic optimized the agentic loop: file reading, tool calling, context management, subagent spawning. Claude Code’s agent is more mature because Anthropic has been iterating on it with internal access OpenCode doesn’t have.

Speed: Nearly Twice as Fast

On identical refactoring tasks, Claude Code averaged 9 minutes vs OpenCode’s 16. The difference comes down to philosophy: Claude Code optimizes for velocity (complete, verify, next), while OpenCode leans toward thoroughness — in test generation, it wrote 94 tests and ran the full 200+ existing suite, whereas Claude Code wrote 73 and only verified those.

For a solo developer against a deadline, that speed matters. For a team with CI/CD, OpenCode’s extra 7 minutes validating regressions can save hours of production debugging.

Advanced Features OpenCode Doesn’t Have

  • Thinking mode: Claude Opus can reason deeply before acting — the difference between an agent that “thinks” and one that “reacts.”
  • Checkpoints: Instant rewind to any previous state. Experiment fearlessly, roll back without git archaeology.
  • Hooks: PreToolUse and PostToolUse — automatic validation and formatting before and after every agent action.
  • Auto-invoked Skills: Skills activate automatically when context requires them, no need to remember to call them.
  • MCP Tool Search: Reduces token usage for tool definitions by 85% (from 134K to 5K tokens).
  • Vim keybindings: If you live in vim/neovim, this alone may decide things.

First-Party Integration

Being the official product has advantages: immediate access to Claude model improvements, first-party tool calling optimization, and a faster feature development cycle.

Where OpenCode Runs Circles Around Claude Code

Model Freedom: The Structural Advantage

Claude Code locks you into Anthropic. OpenCode lets you choose from 75+ providers. This is deeper than it sounds:

  • Cost optimization: Use Claude for complex reasoning, Gemini for bulk processing (free via AI Studio), local models for sensitive code.
  • Real privacy: For code that can’t leave your organization — banking, healthcare, defense — OpenCode + Ollama + a local model is the only viable option. This isn’t niche; it’s enterprise reality.
  • Future-proofing: If a better model emerges tomorrow (and one will), OpenCode users switch a config line. Claude Code users wait for Anthropic to integrate it.

Cost: $0 vs $20-200/month

OpenCode doesn’t charge for the tool. You only pay for API usage of your chosen model.

Claude Code:

  • Pro: $20/month (with usage limits)
  • Max 5x: $100/month
  • Max 20x: $200/month

For an individual developer, $20/month is manageable. For a team of 50, that’s $1,000-10,000 monthly in subscriptions alone, plus API usage. With OpenCode, that tool cost vanishes, and you can also pick cheaper models for simple tasks.

TUI: Visibility Claude Code Doesn’t Offer

OpenCode’s terminal UI is genuinely superior. A sidebar shows live token usage. Subagent status updates in real time. Hotkeys let you undo messages. The buffer system handles unlimited scrolling.

In Claude Code, subagents are invisible. You type a command and wait, with no idea what’s happening behind the scenes. For many devs, this is frustrating — you can’t debug why the agent made a certain decision if you couldn’t see its reasoning.

Native GitHub Actions

Mention /opencode in an issue or PR, and the agent:

  • Classifies the issue and explains what’s happening
  • Creates branches and implements changes
  • Opens PRs with all modifications
  • Responds to inline comments with file and line context

Claude Code can do CI/CD with headless mode, but it requires more orchestration. OpenCode’s integration is more elegant for GitHub-native workflows.

What They Don’t Tell You

The OAuth Block Incident

On January 9, 2026, Anthropic changed its OAuth policy to block third-party applications from authenticating users through Claude.ai accounts. OpenCode was the primary casualty: users who relied on their Claude Pro login lost access overnight.

The official reason was “security and ToS compliance.” The timing — just as OpenCode was gaining traction as a Claude Code alternative — didn’t go unnoticed.

Practical consequence: you can still use Claude models in OpenCode, but you need a direct Anthropic API key, not your Claude.ai account login. The more lasting impact was reputational: it accelerated many users migrating to Gemini as their default backend.

OpenCode’s Uncomfortable Bug

Multiple testers reported that OpenCode, across all tested models (Sonnet 4, Gemini Pro, GPT-4.1), reformats existing code without authorization. On mature codebases with established style guides, this is a trust issue. Claude Code doesn’t do this.

You can mitigate it with rules in AGENTS.md, but it shouldn’t be necessary.

Where They’re Tied

Code quality with the same model: When both use Claude Sonnet 4.5, multiple testers report they “can’t tell the difference.” Output quality is indistinguishable. The difference is in the agent, not the generated code.

Developer experience: Subjective. If you value visibility and control, OpenCode wins. If you value speed and polish, Claude Code wins. There’s no universal winner.

So Is OpenCode a Real Competitor?

Yes, absolutely. It’s not smoke. The GitHub star explosion wasn’t an accident — it was developers fleeing vendor lock-in.

But it’s not a 1:1 replacement. It’s a competitor that wins in different dimensions:

OpenCode wins for:

  • Startups and teams needing cost control
  • Regulated environments where code can’t leave the organization
  • Devs who want to pick the best model per task
  • GitHub-native workflows
  • Those who value visibility into what the agent is doing

Claude Code wins for:

  • Complex projects where an 82% vs 74% success rate matters
  • Devs who need speed over thoroughness
  • Enterprise teams with existing Claude subscriptions
  • Vim users who won’t negotiate their keybindings
  • Those who want the most advanced features (hooks, checkpoints, thinking mode)

The most likely prediction: convergence. Claude Code will add multi-model support under competitive pressure. OpenCode will polish reformatting bugs and improve context management. The real winner is the ecosystem: competition is pushing both to improve faster.

Our Take at Guayoyo Tech

We tested both on real projects. The honest answer: it depends on your stack and priorities. If your code is complex, enterprise, and every percentage point of accuracy counts, Claude Code remains the safer choice. If you need model flexibility, cost control, or to run in privacy-restricted environments, OpenCode is production-ready.

At Guayoyo Tech, we implement AI agent solutions tailored to your company’s actual needs — no vendor dogma, no empty hype. We evaluate your stack, constraints, and goals to recommend what works, not what’s trending.

Want to integrate AI agents into your development workflow? Evaluating whether Claude Code, OpenCode, or another tool is right for your team? Let’s talk — no strings attached.

I built a programming language to test how well LLM-edited code survives

Can programming languages be designed for LLM code edits?

I built a statically typed programming language called Almide.

The motivation was not simply to make another general-purpose language. I wanted to explore a specific question:

If LLMs are going to modify existing code, can programming languages be designed so those edits are less likely to break the program?

Most discussion around AI and programming focuses on code generation: can a model write a function from scratch, solve a benchmark problem, or generate an implementation from a prompt?

But in day-to-day software work, a lot of coding is not greenfield generation. It is modification:

  • add a parameter
  • change a data structure
  • update a parser
  • fix an edge case
  • refactor an API
  • preserve existing behavior
  • make the tests still pass

That led me to a metric I call modification survival rate.

Modification survival rate

The idea is simple:

After an LLM modifies an existing program, does the result still compile and pass the existing tests?

This is intentionally stricter than asking whether the output “looks right.” It asks whether the modified program survives the basic checks that real code has to survive.

For Almide, I built a small benchmark with 30 code modification tasks.

On the current benchmark:

  • Claude Sonnet 4.6 passes 30/30 Almide tasks
  • Running the same task set in Rust gives around 58%

This is not meant as “Almide is better than Rust.”

Rust is vastly more mature, has a much larger ecosystem, and is solving a broader and harder set of problems. I use Rust as a comparison point because it is a serious statically typed systems language with strong compile-time guarantees.

The question I’m trying to explore is narrower:

Can language design choices measurably affect how often LLM-generated edits continue to compile and pass tests?

What Almide is

Almide is a statically typed language implemented in Rust.

It currently has:

  • bidirectional type inference
  • generics
  • pattern matching with exhaustiveness checking
  • effect functions for automatic error propagation
  • pipeline operator and UFCS-style calls
  • a module system with versioned packages
  • native Rust code generation
  • direct WebAssembly code generation
  • a browser playground where the compiler itself runs as WASM

Playground:
https://almide.github.io/playground/

Benchmark:
https://almide.github.io/almide-dojo/

Why language design might matter for LLM edits

LLMs are very good at producing plausible code. The problem is that plausible code is not always valid code.

When a model edits an existing program, it can fail in many small ways:

  • changing one call site but not another
  • returning the wrong variant
  • forgetting an error case
  • breaking ownership or mutation rules
  • producing code that looks locally correct but no longer matches the surrounding program
  • making an edit that compiles but fails tests

Some of these failures are model problems. But some may be language-design problems.

A language can make edits more survivable by making program structure easier to infer, making common changes local, making invalid states easier to reject, and giving diagnostics that point toward the intended repair.

That is the design space Almide is exploring.

The benchmark

The benchmark is built around code modification tasks rather than from-scratch generation.

Each task gives the model an existing program and asks it to modify the code. The output is then checked by compilation and tests.

The score is not based on whether the code is elegant, idiomatic, or human-preferred. The first question is just:

Did the edit survive?

I know 30 tasks is small. I do not think this benchmark proves anything definitive yet.

But the early result is interesting enough that I want to make the benchmark public and get feedback from people who work on programming languages, compilers, testing, and AI-assisted programming.

What I want feedback on

I’m especially interested in feedback on the benchmark methodology.

For example:

  • What kinds of modification tasks should be included?
  • How should task difficulty be categorized?
  • How do we avoid overfitting the language to the benchmark?
  • Should the benchmark include multi-file edits?
  • Should it include larger libraries or real applications?
  • How should we compare across languages fairly?
  • Is “compile and pass tests” enough, or should there be another layer of semantic checking?

I’m also interested in language-design feedback.

If LLM-assisted modification becomes a normal programming workflow, what should languages optimize for?

Not just for humans writing code from scratch, but for humans and machines repeatedly changing existing code together.

That is the question Almide is trying to explore.

OpenCode vs Claude Code: ¿Amenaza Real o Alternativa para Otro Nicho?

OpenCode vs Claude Code — Header

OpenCode vs Claude Code — Header

Si usas agentes de código en la terminal, ya sabes que Claude Code es el rey. Pero en los últimos meses apareció un competidor con números que marean: 160,000 estrellas en GitHub, 900 contribuidores, 13,000 commits, y una propuesta frontal: “lo mismo que Claude Code, pero open source, multi-modelo, y sin cuota mensual”.

Se llama OpenCode. Y la pregunta que todo desarrollador se está haciendo es: ¿realmente compite con Claude Code, o es humo con buena terminal UI?

Probé ambos y leí todo lo que publicaron quienes los probaron en serio. Este artículo no es un benchmark — es una comparación honesta, con las victorias y las derrotas de cada uno sobre la mesa.

¿Qué es cada uno?

OpenCode es un agente de IA open source construido en Go que corre en la terminal, el escritorio y el IDE. Lo instalas en un minuto con curl -fsSL https://opencode.ai/install | bash. Su tesis: tú pones el modelo, OpenCode pone el agente. Soporta 75+ proveedores LLM — desde Claude y GPT hasta modelos locales vía Ollama. No guarda tu código, no te cobra suscripción, y lo usan más de 7.5 millones de desarrolladores al mes.

Claude Code es el agente oficial de Anthropic, lanzado en febrero 2025. Corre exclusivamente modelos Claude (Opus 4.5, Sonnet 4.5, Haiku 4.5). Requiere suscripción ($20/mes Pro, $100/mes Max). Es el benchmark de la industria: el primero en romper el 80% en SWE-bench Verified, el agente que equipos como Doctolib usan para acelerar deploys un 40%.

Diferencias clave de un vistazo:

OpenCode Claude Code
Licencia MIT (open source) Propietaria
Estrellas GitHub 161K 124K
Modelos 75+ (Claude, GPT, Gemini, Ollama…) Solo Anthropic
Costo herramienta $0 $20-200/mes
Interfaz TUI multi-panel Chat terminal
Vim bindings No Sí
Subagentes visibles Sí No (background)
Sidebar de contexto Sí (tokens en vivo) No
GitHub Actions Nativo (/opencode) Headless mode
AGENTS.md Soportado No lo lee

Donde Claude Code sigue siendo el rey

Rendimiento puro del agente

Cuando comparás ambos usando el mismo modelo (Claude Sonnet 4.5), Claude Code gana por 8 puntos porcentuales en tareas complejas. En pruebas independientes con 38 tareas — refactors multi-archivo, bug fixes, features nuevas, generación de tests — Claude Code completó 82% vs 74% de OpenCode.

La diferencia no está en el modelo. Está en cómo Anthropic optimizó el loop agentivo: lectura de archivos, tool calling, gestión de contexto, spawning de subagentes. El agente de Claude Code está más maduro porque Anthropic viene iterando sobre él con acceso interno que OpenCode no tiene.

Velocidad: casi el doble de rápido

En tareas idénticas de refactoring, Claude Code promedió 9 minutos vs 16 minutos de OpenCode. La diferencia es la filosofía: Claude Code optimiza para velocidad (completar, verificar, siguiente), mientras OpenCode tiende a la exhaustividad — en generación de tests escribió 94 tests y corrió el suite completo de 200+ existentes, mientras Claude Code escribió 73 y solo verificó esos.

Para un dev solitario contra un deadline, esa velocidad importa. Para un equipo con CI/CD, los 7 minutos extra de OpenCode validando regresiones pueden ahorrar horas de debugging.

Funcionalidades avanzadas que OpenCode no tiene

  • Thinking mode: Claude Opus puede razonar en profundidad antes de actuar. Es la diferencia entre un agente que “piensa” y uno que “reacciona”.
  • Checkpoints: Rewind instantáneo a cualquier estado anterior. Experimentás sin miedo, volvés atrás sin git archaeology.
  • Hooks: PreToolUse y PostToolUse — validación y formateo automático antes y después de cada acción del agente.
  • Skills auto-invocadas: Se activan solas cuando el contexto las requiere, sin que tengas que acordarte de llamarlas.
  • MCP Tool Search: Reduce el uso de tokens para tool definitions en un 85% (de 134K a 5K tokens).
  • Vim keybindings: Si vives en vim/neovim, esto solo puede definir la elección.

Integración con Anthropic

Ser el producto oficial tiene ventajas: acceso inmediato a las mejoras de los modelos Claude, optimización first-party del tool calling, y un ciclo de desarrollo más rápido en features nuevas.

Donde OpenCode le pasa por encima

Libertad de modelos: la ventaja estructural

Claude Code te encierra en Anthropic. OpenCode te deja elegir entre 75+ proveedores. Esto es más profundo de lo que parece:

  • Optimización de costos: Usás Claude para razonamiento complejo, Gemini para procesamiento bulk (gratis vía AI Studio), modelos locales para código sensible.
  • Privacidad real: Para código que no puede salir de tu organización — banca, salud, defensa — OpenCode + Ollama + un modelo local es la única opción viable. Esto no es un nicho: es requisito enterprise.
  • Future-proofing: Si mañana sale un modelo mejor (y va a salir), los usuarios de OpenCode cambian una línea de configuración. Los de Claude Code esperan a que Anthropic lo integre.

Costo: $0 vs $20-200/mes

OpenCode no cobra por la herramienta. Solo pagás el uso de API del modelo que elijas.

Claude Code:

  • Pro: $20/mes (con límites de uso)
  • Max 5x: $100/mes
  • Max 20x: $200/mes

Para un desarrollador individual, $20/mes es manejable. Para un equipo de 50, son $1,000-10,000 mensuales solo en suscripciones, más el uso de API. Con OpenCode, ese costo de herramienta desaparece y además puedes elegir modelos más baratos para tareas simples.

TUI: visibilidad que Claude Code no ofrece

La terminal UI de OpenCode es genuinamente superior. Panel lateral con uso de tokens en vivo, estado de subagentes en tiempo real, hotkeys para deshacer mensajes, y un sistema de buffer que maneja scrolling ilimitado.

En Claude Code, los subagentes son invisibles. Escribes un comando y esperas, sin saber qué está pasando detrás. Para muchos devs esto es frustrante — no puedes debuggear por qué el agente tomó cierta decisión si no viste su razonamiento.

GitHub Actions nativo

Mencionás /opencode en un issue o PR, y el agente:

  • Clasifica el issue y explica qué está pasando
  • Crea branches e implementa cambios
  • Abre PRs con todas las modificaciones
  • Responde a comentarios inline con contexto de archivo y línea

Claude Code puede hacer CI/CD con headless mode, pero requiere más orquestación. La integración de OpenCode es más elegante para flujos GitHub-nativos.

Lo que no te cuentan

El incidente del OAuth Block

El 9 de enero de 2026, Anthropic cambió su política de OAuth para bloquear que aplicaciones third-party autenticaran usuarios contra cuentas de Claude.ai. OpenCode fue el principal afectado: los usuarios que usaban su login de Claude Pro se quedaron sin acceso de la noche a la mañana.

El motivo oficial fue “seguridad y cumplimiento de ToS”. El timing — justo cuando OpenCode estaba ganando tracción como alternativa a Claude Code — no pasó desapercibido.

Consecuencia práctica: puedes seguir usando modelos Claude en OpenCode, pero necesitas una API key directa de Anthropic, no el login de tu cuenta Claude.ai. El impacto más duradero fue reputacional: aceleró que muchos usuarios migraran a Gemini como backend por defecto.

El bug incómodo de OpenCode

Varios testers reportaron que OpenCode, con todos los modelos probados (Sonnet 4, Gemini Pro, GPT-4.1), reformatea código existente sin autorización. En codebases maduras con guías de estilo establecidas, esto es un problema de confianza. Claude Code no lo hace.

Se puede mitigar con reglas en AGENTS.md, pero no debería ser necesario.

Donde están empatados

Calidad de código con el mismo modelo: Cuando ambos usan Claude Sonnet 4.5, múltiples testers reportan que “no pueden notar la diferencia”. La calidad del output es indistinguible. La diferencia está en el agente, no en el código generado.

Experiencia de desarrollador: Es subjetiva. Si valorás visibilidad y control, OpenCode gana. Si valorás velocidad y polish, Claude Code gana. No hay un ganador universal.

¿Entonces OpenCode es competidor real?

Sí, completamente. No es humo. La explosión de estrellas en GitHub no fue un accidente — fue desarrolladores huyendo del vendor lock-in.

Pero no es un reemplazo 1:1. Es un competidor que gana en dimensiones distintas:

OpenCode gana para:

  • Startups y equipos que necesitan control de costos
  • Entornos regulados donde el código no puede salir de la organización
  • Devs que quieren elegir el mejor modelo para cada tarea
  • Flujos de trabajo GitHub-nativos
  • Quienes valoran visibilidad sobre lo que el agente está haciendo

Claude Code gana para:

  • Proyectos complejos donde el 82% vs 74% de tasa de éxito importa
  • Devs que necesitan velocidad por encima de exhaustividad
  • Equipos enterprise que ya tienen suscripciones Claude
  • Usuarios de vim que no negocian sus keybindings
  • Quienes quieren las features más avanzadas (hooks, checkpoints, thinking mode)

La predicción más probable: convergencia. Claude Code va a agregar soporte multi-modelo bajo presión competitiva. OpenCode va a pulir bugs de reformateo y mejorar su gestión de contexto. El verdadero ganador es el ecosistema: la competencia está forzando a ambos a mejorar más rápido.

Nuestra opinión en Guayoyo Tech

Probamos ambos en proyectos reales. La respuesta honesta: depende de tu stack y de tus prioridades. Si tu código es complejo, enterprise, y cada punto porcentual de accuracy importa, Claude Code sigue siendo la opción más segura. Si necesitas flexibilidad de modelos, control de costos, o correr en entornos con restricciones de privacidad, OpenCode ya está listo para producción.

En Guayoyo Tech implementamos soluciones con agentes de IA ajustados a las necesidades reales de tu empresa — sin dogmas de vendor, sin hype vacío. Evaluamos tu stack, tus restricciones y tus objetivos para recomendarte lo que funciona, no lo que está de moda.

¿Querés integrar agentes de IA en tu flujo de desarrollo? ¿Evaluar si Claude Code, OpenCode u otra herramienta es la correcta para tu equipo? Conversemos sin compromiso.

Skybridge V1.0: THE framework for building MCP apps

New skybridge API

Building an MCP App is a genuinely new problem. You’re not building a website or a standard API; you’re building something that runs inside an AI assistant, interacts with a non-deterministic model, and renders UI within a chat. A year ago, none of that existed. Today, MCP Apps run reliably in Claude and ChatGPT, the official SDK has matured, and +1,000 apps are present in the public app stores.

Earlier this year, we open-sourced Skybridge, a full-stack TypeScript framework for building MCP Apps. It handles the plumbing: the MCP server, view rendering, CSP configuration, dev tools, client compatibility, and the connection between your server logic and your UI components. The goal is to let you focus on what your app does, rather than how to wire it all together.

The response was bigger than we expected. Skybridge now has 100k monthly downloads and powers over 10% of apps on the Claude and ChatGPT stores, with teams ranging from Fortune 500 companies to early-stage startups. That adoption is what’s driving this release.

Today we’re thrilled to introduce Skybridge 1.0, a first-class framework with a stable API, a complete developer toolchain, and a clear commitment to the MCP Apps ecosystem. If you’re building an MCP App, this is the foundation to build on.

Simpler, type-safe API

The goal of Skybridge has always been to abstract away underlying implementation details. In v1, that means one entry point: server.registerTool(config, handler). It replaces the old split between plain tools and tools with a view: include a view in the config if you need one, and Skybridge wires it up. The Vite plugin handles component discovery and bundling automatically. Less boilerplate, fewer wiring mistakes, and a cleaner mental model overall.

Think of Skybridge as the React Native of MCP App development: one codebase, compatible with every environment.

New skybridge API

Notably, we’re the first framework to include strongly typed tool-to-component binding, which is particularly useful when building with agents, where small mistakes are difficult to track down. We’ve also standardized on the term “views” across the framework and docs.

A (really) complete dev loop

One of the hardest things about building MCP Apps has always been the feedback loop. You make a change, tunnel your local server, switch to ChatGPT or Claude, test, and repeat. For every small UI tweak or logic fix, that cycle compounds fast.

In V1, the dev tools have been redesigned from the ground up. They launch automatically when you run skybridge dev, with a layout reflecting how you actually work: views are now first-class in the interface, with a dedicated panel that makes it easy to see what your view looks like and how it responds to tool calls. There’s a mobile preview mode for easy small-screen viewing.

The new and improved Skybridge dev tools, including a familiar helper we snuck into the template you get when you install Skybridge. He's been waiting 25 years for a comeback, and this time, he won't interrupt you.

Most importantly, the dev tools are now a unified control panel for your entire development workflow. From the dev tools header, you can:

  • Open a tunnel to test against a live AI client: Alpic Tunnel exposes your local server via a stable, authenticated URL that works with any MCP-compatible client.

  • Test your app in the Alpic playground, a production-like environment that avoids having to add your app to ChatGPT or Claude. It’s the fastest way to iterate on tool behavior and UI together.

  • Audit your app with Beacon, a full compliance scan of your app against the Claude Connector Directory & ChatGPT App Store. Beacon catches the things that are easy to miss in local testing: CSP misconfigurations, missing tool descriptions, client-specific quirks.

  • Deploy directly to Alpic or wherever your stack lives once you’re ready to ship.

We believe these devtools really combine everything a developer needs to build a production-grade MCP app with no platform or iteration friction.

Deploy everywhere

Lastly, we’ve long believed that a framework’s value shouldn’t come from locking you in.

1.0 makes good on that. Your app ships with a Dockerfile in the template, and the framework is now compatible with Cloudflare Workers. Alpic Cloud remains the fastest path to production if you want zero infrastructure work, but it’s a choice, not a requirement.

In a similar vein, we’ve exposed the Express server giving you full control over the application and the ability to extend it with custom routes or middleware.

Getting started

If you’re migrating an existing project, check out (or hand your coding agent) our release notes at:

https://github.com/alpic-ai/skybridge/releases/tag/v1.0.0

For new projects:

npm create skybridge

What’s next?

V1 is a foundation, not a finish line, and it’s a project we’re investing more time into with every release. On the roadmap:

  • Dev tools: continued improvements, especially around CSP and auth debugging
  • Deployment: broader platform coverage
  • Protocol: simplified auth configuration & expanded client support
  • Agent-friendliness: programmatic devtool APIs, better plugins, and more

Is there something you’d like to see? Open an issue or find us on Discord!

Check out our latest Code with Fred to see the dev tools live in action.