How the Agent-Skills Lockfile System Ensures Skill Integrity and Prevents Tampering

The Agent-Skills repository uses atomic file operations, cryptographic hashes, strict Zod schema validation, and versioned migrations in .skill-lock.json to guarantee that skill metadata cannot be silently corrupted or altered without detection.

The tech-leads-club/agent-skills repository manages complex agent configurations through a centralized lockfile system that safeguards every installation. By combining cryptographic verification with defensive file operations, the lockfile system ensures skill integrity and prevents tampering across both global and project-scoped environments.

Core Integrity Mechanisms

The lockfile implementation in libs/core/src/lib/services/lockfile.service.ts employs multiple defensive layers to maintain an immutable record of installed skills.

Strict Zod Schema Validation

Every lockfile undergoes parsing against the SkillLockFileSchema which defines required fields, strict types, and allowed enum values. When readSkillLock executes, any invalid or malformed data immediately triggers a fallback to an empty lockfile rather than propagating corruption downstream.

This validation occurs at lines 14-31 of lockfile.service.ts, ensuring that only well-formed entries ever persist to disk.

Versioned Migration for Forward Compatibility

The lockfile carries a version number that enables safe schema evolution. When migrateLockFile detects an older version (such as v1), it automatically upgrades the structure to the current schema (v2) and injects missing defaults for fields like method and global.

This mechanism, implemented at lines 49-71, guarantees that legacy files cannot hide unexpected fields or bypass modern validation requirements.

Content-Hash Tracking for Tamper Detection

When addSkillToLock records a new skill (lines 62-68), it optionally stores a contentHash representing the SHA-256 digest of the skill's source files. Consumers can later recompute this hash and compare it against the stored value to detect any manual tampering of skill files after installation.

Atomic Writes with Backup Protection

The writeSkillLock function implements a crash-resistant write pattern at lines 17-33:

  1. Writes new data to a temporary <lock>.tmp file
  2. Backs up the previous lockfile to .skill-lock.json.backup
  3. Renames the temporary file into place atomically

If any step fails, the temporary file is cleaned up and the original lockfile remains untouched, eliminating partial-write corruption risks. This pattern extends to deletion operations through removeAgentFromLock and removeSkillFromLock (lines 106-133), ensuring that all mutations follow the same atomic path.

Scope Isolation and Error Resilience

Beyond integrity checks, the system implements safeguards against accidental scope pollution and corruption scenarios.

Separate Global and Project Scopes

The getSkillLockPath helper (lines 33-37) resolves lockfile locations based on the global flag, preventing cross-scope overwrites. Project-scoped skills write to .agents/.skill-lock.json within the repository, while global installations target ~/.agent-skills/.skill-lock.json.

Graceful Degradation on Corruption

If the lockfile is unreadable, missing, or corrupted, readSkillLock (lines 91-100) returns a fresh empty lockfile instead of throwing exceptions. This allows CLI operations to continue while maintaining a known-good state, with the backup copy (.skill-lock.json.backup) available for manual restoration if needed.

Practical Implementation Examples

The following patterns demonstrate how to interact with the integrity safeguards programmatically.

// Add a new skill with cryptographic hash for integrity verification
await addSkillToLock(
  ports,
  'accessibility',
  ['cursor', 'claude-code'],
  {
    source: 'https://github.com/tech-leads-club/skills/accessibility',
    contentHash: 'a3f5e2...', // SHA-256 of the skill bundle
    method: 'symlink',
    global: false,
    version: '1.0.0',
  },
);
// Verify a skill's integrity by comparing stored hash against current files
import { createHash } from 'node:crypto';
import { readFile } from 'node:fs/promises';

async function verifySkill(ports, skillName) {
  const entry = await getSkillFromLock(ports, skillName);
  if (!entry?.contentHash) return true; // No hash stored → skip check

  const skillPath = join(ports.env.homedir(), '.../skills', skillName);
  const data = await readFile(skillPath);
  const actualHash = createHash('sha256').update(data).digest('hex');

  return actualHash === entry.contentHash;
}
// Safely read lockfile with automatic fallback on corruption
const lock = await readSkillLock(ports, /* global */ false);
console.log('Installed skills:', Object.keys(lock.skills));

Summary

  • Schema validation through SkillLockFileSchema rejects malformed entries before they reach persistent storage.
  • Versioned migrations ensure legacy lockfiles upgrade safely while preventing hidden field injection.
  • SHA-256 content hashes enable cryptographic verification of skill files against tampering.
  • Atomic writes with backup creation eliminate partial-write corruption and preserve historical state.
  • Scope isolation via getSkillLockPath prevents accidental overwrites between global and project contexts.
  • Graceful degradation allows continued operation even when lockfiles are corrupted or missing.

Frequently Asked Questions

What happens if someone manually edits the lockfile?

The SkillLockFileSchema validation in readSkillLock will reject malformed entries on the next read operation, triggering a fallback to an empty lockfile. This prevents corrupted edits from affecting runtime behavior while preserving the backup copy at .skill-lock.json.backup for inspection.

How does the system handle concurrent installations?

The writeSkillLock function uses atomic file operations: it writes to a temporary file first, then renames it into place. This ensures that at most one complete version ever exists on disk, eliminating race conditions where concurrent processes might produce partially written or interleaved data.

Where does the lockfile store its backup copy?

According to libs/core/src/lib/constants.ts, the system creates backups using the pattern .skill-lock.json.backup in the same directory as the original lockfile. This applies to both project-scoped paths (.agents/.skill-lock.json.backup) and global locations (~/.agent-skills/.skill-lock.json.backup).

Can the lockfile detect if skill source files were modified after installation?

Yes, when skills are added via addSkillToLock, the optional contentHash field stores a SHA-256 digest of the skill's source files. Verification tools can recompute this hash and compare it against the stored value to detect any post-installation tampering or accidental modification.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →