Protocol Specification
The lock file format used by File-Based Semaphore (TypeScript).
Overview
This tool uses the same lock file format as the Rust File-Based Semaphore, enabling cross-language process coordination.
Lock File Format
Lock files are plain text with key-value pairs:
pid=<process_id>
timestamp=<unix_timestamp>
tag=<optional_description>Fields
| Field | Type | Required | Description |
|---|---|---|---|
pid | integer | Yes | Process ID of the lock holder |
timestamp | integer | Yes | Unix timestamp (seconds) when acquired |
tag | string | No | Optional description/identifier |
Example Lock File
pid=12345
timestamp=1735420800
tag=npm buildLine Endings
Both Unix (\n) and Windows (\r\n) line endings are accepted when parsing. Lock files are always written with Unix line endings.
Character Encoding
- Lock files use UTF-8 encoding
- Tags may contain Unicode characters
- Control characters in tags are replaced with spaces
Atomic Operations
Lock Acquisition
- Create temp file:
<path>.<pid>.<random>.tmp - Write lock info to temp file
- Attempt atomic rename to final path
- On success: lock acquired
- On failure (file exists): lock held by another process
Lock Release
- Verify lock file exists
- Read lock file content
- Verify PID matches (unless force)
- Delete lock file
Stale Lock Detection
A lock is considered stale when:
- The holding PID no longer exists (process check)
- AND the timestamp is older than
staleTimeout
The second condition prevents false positives when:
- PIDs are reused quickly after process death
- System clocks are skewed
Default stale timeout: 1 hour (3600000ms)
Security Considerations
Path Traversal Prevention
Paths containing .. are rejected to prevent directory traversal attacks.
Symlink Resolution
Symlinks are resolved to their target paths, including dangling symlinks.
Tag Sanitization
All control characters (0x00-0x1F, 0x7F) are replaced with spaces to prevent:
- Newline injection (fake key-value pairs)
- Terminal escape sequence injection
- Null byte attacks
Temp File Security
- Temp files use cryptographic random suffixes
- Created with restrictive permissions (0600)
- Orphaned temp files are cleaned by
cleanStale()
Compatibility
Rust sema Tool
This format is fully compatible with the Rust sema tool:
# TypeScript creates lock
semats try-acquire /tmp/shared.lock --tag "ts-process"
# Rust reads it
sema status /tmp/shared.lock
# Output:
# locked: true
# pid: 12345
# timestamp: 1735420800
# tag: ts-processVersion Compatibility
The format is designed to be forward-compatible:
- Unknown keys are ignored when parsing
- Future versions may add optional fields
Parsing Rules
- Split content by newlines (
\nor\r\n) - For each line, split by first
= - Trim whitespace from key and value
- Skip empty lines and lines without
= pidandtimestampmust be valid integerstagis optional; missing means no tag
Error Cases
Parsing fails if:
pidis missing or non-numerictimestampis missing or non-numeric- File is empty or contains only whitespace
- File contains binary garbage
Implementation Notes
Atomic Rename
The temp file + rename pattern provides atomicity on most filesystems:
- POSIX:
rename()is atomic within same filesystem - Windows:
MoveFileExwithMOVEFILE_REPLACE_EXISTING
Known Limitations
- TOCTOU Race: Time-of-check to time-of-use gap between checking lock existence and creating it. Mitigated but not eliminated by atomic rename.
- Network Filesystems: NFS and other network filesystems may not guarantee rename atomicity.
- File Permissions: Depend on filesystem support.