Overview

Denju

A Rust registry and synchronization system for Agent Skills. Subscribe once, keep the right skills on every machine, and preserve both sides when concurrent edits disagree.

RustPostgreSQLCLI
Agent SkillsSyncS3
Denju

I kept sharing the same agent skills with teammates, then asking agents to check GitHub to see whether the copies we had were still current. A skill is just a directory, which is a good interface until that directory exists on several machines and everybody has edited a different copy.

Denju is what I wanted in the middle: some of the discovery of GitHub stars and an npm-style registry, some of the synchronization of Drive, and subscriptions that turn “this skill looks useful” into “keep the right version on my machines.” Those are inspirations, not integrations. The product is a Rust CLI and daemon, a registry, and ordinary Agent Skills on disk.

Keep the interface the agents already understand

I didn’t introduce another skill format or an API the agent has to call at runtime. Denju manages a canonical local tree and projects directory links into the places Codex and Claude Code already discover skills: the shared agents directory and Claude’s configured skills directory. The agent sees a normal skill. Denju owns how that copy got there and how it stays current.

Public search and subscriptions don’t require an account. An identity becomes useful when I want ownership, private multi-device sync, publishing, sharing, or teams. Stars and author follows help with discovery; they don’t silently install anything. Subscriptions and assigned packs are what change the desired local set.

That distinction carries through editing. My own imported skills start private, and valid saves can synchronize between my signed-in devices without a public release. Editing somebody else’s subscribed skill preserves the change as a fork. Publishing is a separate action that creates an immutable numbered release.

Merkle content and revisions

Underneath that simple interface, every file is a SHA-256-addressed blob. Sorted tree objects record names, child identities, executable bits, and relative symlink targets. A revision names the root tree, its parents, and the author and operation identities. Change one file and its blob and ancestor trees change identity; untouched content keeps the same identity.

This is what makes “the same version” a property the system can verify. A numbered release points permanently at one revision, while the latest-release pointer can move. Restoring an older state creates a new revision instead of rewriting history. The tar-and-zstd snapshot used for transport is a verified representation of that content, not the thing that defines the revision.

Deduplication also has a permission boundary. The registry doesn’t let one namespace claim a blob merely because another namespace has already stored the same hash. A namespace has to first prove its own content by uploading verifiable bytes before it can reference them. Physical storage can be deduplicated while reachability and quota accounting stay scoped to the owner. Sharing bytes shouldn’t accidentally mean sharing access.

Do less work without weakening the check

The local SQLite file index reuses known hashes when size, modification time, and executable state still match, with forced verification scans to catch changes that metadata alone can miss. Preparing a revision asks which blobs that namespace still needs to prove, so the uploader sends the missing ones.

Uploads reuse known blobs; incoming updates currently fetch complete verified skill snapshots. The content model gives stable identities and deduplication; the implementation chooses where incremental work is already useful and where a complete verified snapshot keeps installation simpler.

A skill becomes visible after it is complete

An agent can be reading a skill while Denju updates it. Updating files one by one in place can expose half of one revision and half of another.

For each skill, Denju stages a complete generation off to the side, validates it against the Merkle manifest, verifies the result, and switches the canonical directory link with an atomic rename. A SQLite journal records the installation stages. Recovery can discard an incomplete stage or finish a verified switch after a crash, rather than guessing from whichever directories happen to exist.

The writable working view is separate from the immutable content store. That lets normal edits remain normal edits without corrupting the stored bytes used to verify a revision. Harness links are projections of this managed state, so a broken link can be repaired without inventing a second source of truth. The atomic boundary is one skill’s installation, not a promise that an entire pack changes in one filesystem transaction.

Watchers tell the daemon to look; they don’t define the truth

Filesystem notifications and the registry’s SSE stream wake the daemon up. They aren’t a history it must replay perfectly to stay correct. The client compares the generations and revision heads it knows with the registry’s current roots, then reconciles what should be installed. Bursts of local saves are debounced, with polling fallback and periodic forced hashing when notifications aren’t enough.

That model is useful offline too. Valid local edits queue first and upload when connectivity returns. If two devices edit the same starting revision, a compare-and-swap failure preserves the divergent heads. A deterministic three-way merge combines disjoint edits; overlapping changes become an explicit conflict for that skill. Resolving it creates a two-parent revision, so neither side disappears from history. An unrelated skill can keep synchronizing while that one waits.

A team gets a policy, not another folder to pass around

Packs group skills with follow-latest or pinned-version policies. Assign a pack to a team and it becomes part of the desired skill set for current members and people who join later. Changing that assignment changes what Denju reconciles, instead of asking everyone to remember another installation step.

Team-owned skills are private by default. Maintainer drafts stay private until release, and making the skill public is explicit. If equally strong policies demand incompatible versions, Denju reports the disagreement and preserves a valid installed version where possible; it doesn’t guess which team should win.

The registry keeps references, permissions, relationships, generations, and its transactional outbox in PostgreSQL, with blobs and snapshots in S3-compatible storage. The same Rust server backs the hosted registry and the self-hosted Docker image. A reference Compose setup bundles the surrounding services, but existing Postgres and object storage work too.

Auto-updating a skill still means trusting its publisher. Denju verifies content and controls distribution; it doesn’t scan instructions for safety or sandbox what an agent does with them. Private sync is access-controlled, not end-to-end encrypted. Within that scope, the aim is straightforward: stop spending time moving skill folders around, without losing track of whose version they are or what happens when the copies disagree.

Use ← and → to navigate