# How Versioning Works in Open-SEO: A Complete Technical Guide

> Discover how versioning works in Open-SEO. This guide details the multi-layered strategy using package.json as the source of truth for telemetry, MCP, R2 storage, and releases.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: deep-dive
- Published: 2026-07-26

---

**Open-SEO implements a multi-layered versioning strategy that uses [`package.json`](https://github.com/every-app/open-seo/blob/main/package.json) as the single source of truth, propagating version identifiers through telemetry payloads, MCP protocol headers, immutable R2 data storage, and automated release workflows.**

The **every-app/open-seo** repository maintains strict versioning across code, data, and infrastructure layers. Understanding how these version numbers interact ensures reliable deployments, accurate upgrade detection, and traceable data history.

## Canonical Version Source in package.json

The **canonical version** lives in the root [`package.json`](https://github.com/every-app/open-seo/blob/main/package.json) file (e.g., `"version": "0.0.11"`). This serves as the single source of truth that the entire application references when it needs to identify its own release identity.

Rather than hard-coding version strings throughout the codebase, Open-SEO imports this value directly from [`package.json`](https://github.com/every-app/open-seo/blob/main/package.json) wherever runtime version awareness is required.

## Runtime Version Propagation

The application propagates the canonical version through two critical runtime channels: telemetry reporting and MCP client transport.

### Telemetry and Upgrade Detection

In [`src/server/lib/self-host-telemetry.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/self-host-telemetry.ts), the version is imported from [`package.json`](https://github.com/every-app/open-seo/blob/main/package.json) and included in every telemetry payload sent by self-hosted instances:

```typescript
// src/server/lib/self-host-telemetry.ts
import { version } from "../../../package.json";

export async function sendTelemetry(state: TelemetryState) {
  const payload = {
    version,                     // <-- current app version from package.json
    ...state,
  };
  // ...send payload to telemetry endpoint
}

```

The database schema in [`src/db/telemetry.schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/telemetry.schema.ts) stores this value in a `lastVersion` column. When a self-hosted instance starts, the system compares the current [`package.json`](https://github.com/every-app/open-seo/blob/main/package.json) version against the stored value to detect upgrades automatically.

### MCP Protocol Headers

The MCP client transport layer hard-codes the version string in [`src/server/mcp/transport.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/transport.ts) to ensure protocol compatibility:

```typescript
// src/server/mcp/transport.ts
export const client = new Client({
  name: "open-seo",
  version: "0.0.11",           // <-- matches package.json version
});

```

This value is sent in the `mcp-protocol-version` header on every request, allowing the server to validate client compatibility before processing commands.

## Immutable Data Versioning for Project Context

Open-SEO treats project context data as an **append-only log** with immutable versioning. According to [`specs/0006-onboarding-agent-implementation.md`](https://github.com/every-app/open-seo/blob/main/specs/0006-onboarding-agent-implementation.md), each version is stored as a markdown blob in Cloudflare R2 under `project-context/{projectId}/{versionId}.md`.

The system records metadata in a `project_context_versions` table:

```typescript
// Conceptual implementation from onboarding spec
async function persistProjectContext(md: string, note?: string) {
  const versionId = crypto.randomUUID();        // immutable version identifier
  await r2.put(`project-context/${projectId}/${versionId}.md`, md);
  await db.insert('project_context_versions', {
    projectId,
    versionId,
    note,
    createdAt: new Date(),
  });
}

```

This architecture allows the UI to retrieve any historic snapshot of a project's context and perform safe rollbacks without mutating previous records.

## Release Automation and Changelog Management

Release artifacts follow a strict naming convention tied to the [`package.json`](https://github.com/every-app/open-seo/blob/main/package.json) version. Each release gets a dedicated Markdown file in the `release-notes/` directory (e.g., [`release-notes/v0.0.26.md`](https://github.com/every-app/open-seo/blob/main/release-notes/v0.0.26.md)).

The workflow defined in [`.opencode/command/release-notes.md`](https://github.com/every-app/open-seo/blob/main/.opencode/command/release-notes.md) automates creation of these files:

```bash

# Workflow extracts version from package.json

VERSION=$(jq -r .version package.json)
cat <<EOF > release-notes/v${VERSION}.md

# Open-SEO ${VERSION}

Full Changelog: https://github.com/every-app/open-seo/compare/v${PREV}...v${VERSION}
EOF
git add release-notes/v${VERSION}.md
git commit -m "release: v${VERSION}"

```

CI pipelines use the filename to publish GitHub releases after merge, ensuring human-readable documentation stays synchronized with the codebase.

## Summary

- **[`package.json`](https://github.com/every-app/open-seo/blob/main/package.json)** serves as the single source of truth for the application version number.
- **Self-host telemetry** ([`src/server/lib/self-host-telemetry.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/self-host-telemetry.ts)) imports this version to track deployments and detect upgrades via [`src/db/telemetry.schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/telemetry.schema.ts).
- **MCP transport** ([`src/server/mcp/transport.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/transport.ts)) hard-codes a matching version string for protocol negotiation.
- **Project context data** uses immutable versioning in R2 with an append-only log pattern defined in the onboarding spec.
- **Release notes** are generated automatically from the [`package.json`](https://github.com/every-app/open-seo/blob/main/package.json) version and stored in `release-notes/` following semantic naming conventions.

## Frequently Asked Questions

### How does Open-SEO detect when a self-hosted instance has upgraded?

The system compares the current version imported from [`package.json`](https://github.com/every-app/open-seo/blob/main/package.json) against the `lastVersion` value stored in the `telemetry` table. When [`src/server/lib/self-host-telemetry.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/self-host-telemetry.ts) sends a startup payload, the server checks for mismatches to identify upgrade events and can trigger automatic migration steps.

### Why is the MCP version hard-coded in transport.ts instead of imported from package.json?

The MCP client version in [`src/server/mcp/transport.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/transport.ts) is hard-coded as `"0.0.11"` to ensure the transport layer maintains strict protocol compatibility guarantees. This prevents accidental protocol mismatches that could occur if the version were dynamically imported during runtime bundling or if the transport module were distributed separately.

### How does Open-SEO handle versioned data storage for project contexts?

According to [`specs/0006-onboarding-agent-implementation.md`](https://github.com/every-app/open-seo/blob/main/specs/0006-onboarding-agent-implementation.md), the system stores each project context version as an immutable markdown blob in R2 under `project-context/{projectId}/{versionId}.md`. An append-only log table tracks these versions, enabling time-travel queries and safe reversion without data mutation, as each write creates a new UUID-versioned blob rather than updating existing files.

### Where does Open-SEO store its release notes and how are they automated?

Release notes live in the `release-notes/` directory with filenames matching the [`package.json`](https://github.com/every-app/open-seo/blob/main/package.json) version (e.g., [`v0.0.26.md`](https://github.com/every-app/open-seo/blob/main/v0.0.26.md)). The workflow in [`.opencode/command/release-notes.md`](https://github.com/every-app/open-seo/blob/main/.opencode/command/release-notes.md) reads the version from [`package.json`](https://github.com/every-app/open-seo/blob/main/package.json) to generate new files automatically, which CI then uses to create GitHub releases after merge.