Skip to main content

Lockfile (skills.lock)

The skills.lock file ensures reproducible installs by pinning exact versions and integrity hashes of all installed skills. It’s automatically generated by tank install and should be committed to version control.

Purpose

Lockfiles solve three critical problems:
  1. Reproducibility — The same skills.lock always installs the exact same versions
  2. Security — SHA-512 hashes verify package integrity and prevent tampering
  3. Performance — Resolved versions are cached, speeding up subsequent installs
Always commit skills.lock to version control. This ensures everyone on your team and your CI/CD pipeline install identical dependencies.

Schema

The lockfile is validated using Zod. Here’s the schema from packages/shared/src/schemas/skills-lock.ts:
import { z } from 'zod';
import { permissionsSchema } from './permissions.js';

export const lockedSkillSchema = z.object({
  resolved: z.string().url(),
  integrity: z.string().regex(/^sha512-/, 'Integrity must start with sha512-'),
  permissions: permissionsSchema,
  audit_score: z.number().min(0).max(10).nullable(),
});

export const skillsLockSchema = z.object({
  lockfileVersion: z.literal(1),
  skills: z.record(z.string(), lockedSkillSchema),
});

export type LockedSkill = z.infer<typeof lockedSkillSchema>;
export type SkillsLock = z.infer<typeof skillsLockSchema>;

Structure

lockfileVersion

Type: 1 (literal) Lockfile format version. Currently, only version 1 is supported. This field allows for future breaking changes to the lockfile format.

skills

Type: Record<string, LockedSkill> A map from skill name (with version) to locked metadata. Key format: {name}@{version} (e.g., @tank/[email protected]) Value (LockedSkill):
  • resolved — Tarball download URL
  • integrity — SHA-512 hash of the tarball (Base64-encoded)
  • permissions — Declared permissions (copied from skills.json)
  • audit_score — Security audit score (0-10, or null if not scanned)

Example

{
  "lockfileVersion": 1,
  "skills": {
    "@tank/[email protected]": {
      "resolved": "https://tankpkg.dev/tarballs/@tank/google-sheets-2.1.0.tgz",
      "integrity": "sha512-abc123...",
      "permissions": {
        "network": {
          "outbound": ["*.googleapis.com", "accounts.google.com"]
        },
        "filesystem": {
          "read": ["./**"]
        },
        "subprocess": false
      },
      "audit_score": 9.2
    },
    "@tank/[email protected]": {
      "resolved": "https://tankpkg.dev/tarballs/@tank/google-auth-2.0.5.tgz",
      "integrity": "sha512-def456...",
      "permissions": {
        "network": {
          "outbound": ["accounts.google.com", "oauth2.googleapis.com"]
        },
        "subprocess": false
      },
      "audit_score": 9.5
    }
  }
}

SHA-512 Integrity Verification

Tank uses SHA-512 (via Subresource Integrity format) to verify package integrity:

Format

sha512-<base64-encoded-hash>
Example:
sha512-z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==

Verification Process

When you run tank install:
  1. Download tarball from resolved URL
  2. Compute SHA-512 hash of downloaded file
  3. Compare computed hash with integrity field
  4. Reject if hashes don’t match (tampering detected)
  5. Extract only if verification passes
Never skip integrity verification. If a hash mismatch occurs, it means the downloaded package doesn’t match what was originally installed. This could indicate:
  • Network tampering or man-in-the-middle attack
  • Compromised registry
  • Corrupted download
Tank will abort the install and display an error.

Lockfile Generation

The lockfile is automatically created or updated by:

tank install

Installs dependencies from skills.json and generates/updates skills.lock:
# Fresh install (no lockfile exists)
tank install
# → Resolves versions, downloads packages, generates skills.lock

# Install with existing lockfile
tank install
# → Uses locked versions, verifies integrity hashes

tank add <skill>

Adds a new dependency and updates the lockfile:
tank add @tank/google-sheets
# → Adds to skills.json, resolves version, updates skills.lock

tank remove <skill>

Removes a dependency and updates the lockfile:
tank remove @tank/google-sheets
# → Removes from skills.json and skills.lock

Reproducible Installs

Lockfiles guarantee that everyone gets the same dependencies:

Without Lockfile

// skills.json
{
  "skills": {
    "@tank/google-sheets": "^2.0.0"
  }
}
Problem: Different team members might get different versions:
  • Developer A runs tank install on Monday → gets 2.1.0
  • Developer B runs tank install on Friday → gets 2.2.0 (new release)

With Lockfile

// skills.lock
{
  "lockfileVersion": 1,
  "skills": {
    "@tank/[email protected]": { ... }
  }
}
Solution: Both developers get 2.1.0 because the lockfile pins the exact version.

Updating Dependencies

To update dependencies while respecting semver ranges:

Update All Dependencies

tank update
# Resolves latest versions within semver ranges
# Updates skills.lock with new versions and hashes

Update Specific Skill

tank update @tank/google-sheets
# Updates only google-sheets and its dependencies

Force Latest Version

tank add @tank/google-sheets@latest
# Updates skills.json to latest version
# Updates skills.lock with new locked entry
Yes, always commit skills.lock to Git. This ensures:
  1. Reproducible installs across your team
  2. Reproducible CI/CD builds
  3. Security auditing — you can verify what was actually installed
  4. Rollback capability — if a dependency causes issues, you can revert the lockfile
The only exception: Do NOT commit lockfiles for library packages (skills that are dependencies of other skills). For libraries, you want to test against the latest compatible versions.

Lockfile Conflicts

When merging branches, lockfile conflicts can occur:

Resolving Conflicts

  1. Don’t manually edit — Let Tank regenerate it
  2. Merge skills.json first — Resolve conflicts in the manifest
  3. Regenerate lockfile:
    rm skills.lock
    tank install
    
  4. Commit the regenerated lockfile

Automatic Conflict Resolution

Some Git tools can auto-resolve lockfile conflicts:
# .gitattributes
skills.lock merge=tank-lockfile
Tank provides a merge driver:
git config merge.tank-lockfile.driver "tank install --merge-lockfile"

Audit Scores in Lockfile

The audit_score field tracks the security score of each locked skill:
{
  "@tank/[email protected]": {
    "resolved": "...",
    "integrity": "...",
    "permissions": { ... },
    "audit_score": 9.2
  }
}
Score Range: 0-10 (higher is better)
  • 10: Perfect — no findings
  • 8-9: Excellent — minor notes
  • 6-7: Good — some medium findings
  • <6: Concerning — high or critical findings
null: Not yet scanned (first install before scan completes) See Security Scanning for how scores are computed.

Permissions Snapshot

The lockfile stores a snapshot of permissions declared in each skill’s manifest:
{
  "permissions": {
    "network": {
      "outbound": ["*.googleapis.com"]
    },
    "filesystem": {
      "read": ["./**"],
      "write": ["./output/**"]
    },
    "subprocess": false
  }
}
Why snapshot permissions?
  1. Audit trail — You can see what permissions were declared at install time
  2. Diff detection — Compare lockfile to detect permission changes in updates
  3. Offline validation — No need to fetch manifest to check permissions
If a skill updates its permissions in a new version, tank update will show a diff and prompt for confirmation before updating the lockfile.

Lockfile Version History

Tank currently uses lockfileVersion: 1. Future versions may introduce breaking changes:
VersionStatusChanges
1CurrentInitial format with SHA-512, permissions, audit scores
2PlannedTBD (may add dependency tree, CVE data, etc.)

Next Steps

Build docs developers (and LLMs) love