Skip to content

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

FieldTypeRequiredDescription
pidintegerYesProcess ID of the lock holder
timestampintegerYesUnix timestamp (seconds) when acquired
tagstringNoOptional description/identifier

Example Lock File

pid=12345
timestamp=1735420800
tag=npm build

Line 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

  1. Create temp file: <path>.<pid>.<random>.tmp
  2. Write lock info to temp file
  3. Attempt atomic rename to final path
  4. On success: lock acquired
  5. On failure (file exists): lock held by another process

Lock Release

  1. Verify lock file exists
  2. Read lock file content
  3. Verify PID matches (unless force)
  4. Delete lock file

Stale Lock Detection

A lock is considered stale when:

  1. The holding PID no longer exists (process check)
  2. 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.

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:

bash
# 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-process

Version Compatibility

The format is designed to be forward-compatible:

  • Unknown keys are ignored when parsing
  • Future versions may add optional fields

Parsing Rules

  1. Split content by newlines (\n or \r\n)
  2. For each line, split by first =
  3. Trim whitespace from key and value
  4. Skip empty lines and lines without =
  5. pid and timestamp must be valid integers
  6. tag is optional; missing means no tag

Error Cases

Parsing fails if:

  • pid is missing or non-numeric
  • timestamp is 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: MoveFileEx with MOVEFILE_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.

Released under the MIT License.