lockayy
v0.1.1
Published
chmod for lines of code. sudo for autonomous agents.
Maintainers
Readme
What it does
Agent can read files. Agent cannot write files directly. Agent can propose edits. lockay validates them. Agent can propose commands. cmdlock gates them.
Two enforcement layers, one binary:
| Layer | Problem | Solution | |-------|---------|----------| | linelock | Agent rewrites wrong code | Lock file regions by content hash | | cmdlock | Agent runs destructive commands | Policy gate with risk levels L0-L4 |
Install
Note: Windows is not supported natively. lockay relies on POSIX APIs. Use WSL on Windows.
npm:
npm install -g lockayy
lockay initInstall script (Linux / macOS / BSD):
curl -fsSL https://raw.githubusercontent.com/Steven-ZN/lockay/main/install.sh | shBuild from source (gcc + make required):
git clone https://github.com/Steven-ZN/lockay.git
cd lockay && make
sudo make install # system-wide
make install-local # user-local (~/.local/bin)Zero library dependencies beyond a C11 compiler.
Quick start (60 seconds)
cd /your/project
# Initialize lockay (creates .lockay/ and .linelock/)
lockay init
# Generate command policy
lockay policy
# Lock a range of lines by line number
lockay lock src/api.py 40 80 you "public contract"
# Format: lockay lock <file> <start_line> <end_line> [owner] [reason]
# View what is locked
lockay status # all files
lockay status src/api.py # one file
# Show file with lock marks (locked lines are marked)
lockay show src/api.py
# Unlock by lock ID (the 6-char ID shown in status)
lockay unlock a1b2c3
# Edit with lock enforcement
lockay edit src/model.py
# Run commands through the gate
lockay run "pytest tests/"
lockay run "rm -rf build/" # prompts for approval
lockay run "git push origin main" # denied by defaultLock vs unlock at a glance:
| Action | Command | Notes |
|--------|---------|-------|
| Lock lines | lockay lock <file> <start> <end> [owner] [reason] | Returns a 6-char lock ID |
| See locks | lockay status [file] | Lists all locks with their IDs |
| See content | lockay show <file> | Locked lines show visual markers |
| Unlock | lockay unlock <lock_id> | Use the ID from status |
| Verify | lockay check <file> | Checks no lock content has been modified |
| Restore | lockay restore <file> | Auto-restore tampered locked lines from backup |
| Watch | lockay watch [interval] | Daemon: monitor and auto-restore on changes |
How linelock works
Locks are stored in .linelock/locks (plain text, git-trackable):
src/api.py|a1b2c3|40|80|sha256:abc123...|sha256:def456...|sha256:ghi789...|you|2026-06-07|public contractEach lock records three SHA-256 hashes:
| Hash | Content | |------|---------| | content_hash | Locked region content | | before_hash | 3 lines before the lock | | after_hash | 3 lines after the lock |
When line numbers shift (inserts/deletes elsewhere in the file), lockay re-anchors the lock by finding its content hash. The lock protects content, not position.
Before edit: After edit:
1: import sys 1: import sys
2: import os 2: import os (new)
3: 3: import re (new)
4: [LOCKED] def api(): 4:
5: [LOCKED] return 42 5: [LOCKED] def api(): <-- re-anchored
6: [LOCKED] 6: [LOCKED] return 42
7: [LOCKED]How cmdlock works
Command lock does not use a CLI subcommand like file lock. Instead, you edit the policy file directly:
| Step | What | Command |
|------|------|---------|
| Generate | Create default policy file | lockay policy |
| Edit | Add your rules | Edit .lockay/policy |
| Enforce | Run commands through the gate | lockay run "..." |
| Monitor | Review all decisions | Check .lockay/audit.log |
Policy format: <command_pattern> <allow|ask|deny> — first match wins.
Commands are classified into 5 risk levels:
L0 SAFE ........ ls, cat, grep, echo, find, wc, diff
L1 EXEC ........ python, make, gcc, node, pytest
L2 WRITE ....... touch, mkdir, cp, mv, git add
L3 DESTRUCT .... rm, pip install, curl, git commit
L4 PUBLISH ..... git push, ssh, sudo, chmod, killPolicy rules in .lockay/policy:
ls * allow # L0: always safe
pytest * allow # L1: always safe
pip install * ask # L3: ask the user
rm -rf * ask # L3: ask the user
git push * deny # L4: never allowed
ssh * deny # L4: never allowed
sudo * deny # L4: never allowedWhen a command hits an ask rule, lockay prompts interactively:
--- lockay: command gate ---
Command : rm -rf ./checkpoints
Risk : L3 (DESTRUCT)
Policy : ASK
-----------------------------
This command requires approval.
Options: [A]llow once [D]enyAll decisions are written to .lockay/audit.log.
Agent-facing CLI
Designed for LLM tool-calling. Structured exit codes:
lockay show src/main.c 100 180 # view with lock annotations
lockay set src/main.c 150 "new text" # change a line
lockay insert src/main.c 100 "new line" # insert before line
lockay delete src/main.c 100 105 # delete range
lockay apply src/main.c /tmp/patch.diff # apply unified diff
lockay run "pytest tests/" # run through policy gate| Exit code | Meaning | |-----------|---------| | 0 | Success | | 1 | Denied (outside editable region, or blocked by policy) | | 2 | Error (file not found, parse error) |
Security model
.---------------. .---------------.
| agent user | | codeguard |
| (read only) | | (owns files) |
'-------^-------' '-------^-------'
| |
| agent invokes | lockay validates
| lockay CLI | then writes
| |
.-------'--------------------------'-------.
| lockay binary |
| |
| load file -> validate locks -> atomic |
| write (temp + fsync + rename) |
| |
| Trusted core never calls: |
| system() popen() exec() shell() |
'-------------------------------------------'For production: chown codeguard:codeguard repo/ + chmod 755 repo/. Agents run as a different user with read-only access. All writes go through lockay.
For local dev: lockay enforces policy at the application level. No privilege separation needed.
TUI Editor
lockay edit <file>Opens a nano-style terminal editor with lock enforcement. Locked lines show a visual marker and cannot be modified.
GNU linelock 0.1 demo.py
──────────────────────────────────────────────────
1 class Model(nn.Module):
█ 2 def __init__(self, dim): [lock-id]
█ 3 super().__init__()
4 self.encoder = nn.Linear(dim, 64)
5
6 def forward(self, x):
7 x = self.encoder(x)
8 return x
──────────────────────────────────────────────────
^G Help ^O Write ^X Exit ^L Lock ^U Unlock| Key | Action |
|-----|--------|
| ^O | Save (validates all locks before writing) |
| ^X | Exit |
| ^L | Lock the current line |
| ^U | Unlock the current line |
| ^K | Cut current line (clears content) |
| ^A / ^E | Jump to start / end of line |
| Arrows | Move cursor |
| Type | Insert characters (rejected on locked lines) |
| Backspace / Delete | Delete characters (rejected on locked lines) |
| Enter | Line break (rejected on locked lines) |
If you try to edit a locked line, it prints: Line N is locked (lock-id) and rejects the change. Save validates all lock content hashes before writing to disk.
Project layout
lockay/
src/
main.c CLI dispatch (14 subcommands)
tui.c nano-style terminal editor
filebuf.c line buffer + char-level editing
lockdb.c lock metadata store
validate.c content-hash validation + re-anchoring
apply.c secure write path (atomic)
cmdlock.c command gate (policy engine + audit)
sha256.c embedded SHA-256 (public domain)
tests/
test_runner.c 51 test cases
install.sh interactive installer (EN/ZH)
Makefile