Skip to content
This repository was archived by the owner on May 23, 2026. It is now read-only.

neuxdotdev/lcurs-administrator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lcurs-administrator

Crates.io License Rust CI

lcurs-administrator is a comprehensive command-line administration toolkit for the LCURS ecosystem. It provides secure key management, file encryption/decryption using Age, an encrypted account registry, TOTP (time-based one-time password) support, API token generation and verification, and a robust authentication system.

This document covers everything: installation, usage, command reference, security model, architecture, API, and development guide.


Table of Contents

  1. Overview
  2. Installation
  3. Quick Start
  4. Architecture & Data Flow
  5. Command Reference
  6. Environment Variables
  7. Security Model
  8. API Reference for Library Users
  9. Development Guide
  10. License

Overview

lcurs-admin is the administrative CLI tool for the LCURS project. Its primary features are:

  • Age encryption/decryption – uses the modern age encryption format (X25519 + ChaCha20‑Poly1305).
  • Encrypted account registry – stores accounts (name, public key, linked private key path, metadata, OTP secret, API token) in an encrypted file (meta.bin), protected with a master secret.
  • TOTP (RFC 6238) – generate secrets, produce OTP codes, and verify them with drift tolerance.
  • API token management – generate, show, revoke hex tokens (32 bytes) per account.
  • Authentication flow – verify API token + OTP for application‑level authentication.
  • Secure configuration storage – an optional encrypted config.bin to persist environment settings (LCURS_KEYS_DIR, LCURS_MASTER_SECRET).

All commands produce structured JSON output (pretty‑printed by default), making it easy to integrate with scripts and other tools. Logging goes to stderr and can be controlled via verbosity flags or RUST_LOG.


Installation

From crates.io

cargo install lcurs-administrator

From source

git clone https://github.com/neuxdotdev/lcurs-administrator.git
cd lcurs-administrator
cargo install --path .

After installation, the binary is named lcurs-admin.

System requirements

  • Rust 1.70 or later (for building from source)
  • Unix-like operating system (Linux, macOS) – Windows is not fully tested, but basic functionality may work (file permissions are not enforced on Windows).

Quick Start

This section walks you through a typical workflow: initializing keys, creating an account, enabling OTP, generating an API token, and verifying authentication.

# 1. Set the mandatory master secret (used to encrypt the account registry)
export LCURS_MASTER_SECRET=$(openssl rand -hex 32)

# 2. Generate a default Age key pair (private and public)
lcurs-admin init

# 3. Encrypt a test file using the default recipient
echo "sensitive data" > secret.txt
lcurs-admin encrypt -i secret.txt -o secret.age

# 4. Decrypt it back
lcurs-admin decrypt -i secret.age -o secret.dec
cat secret.dec   # prints "sensitive data"

# 5. Create an account named "alice" with the default public key
#    (the default recipient from the init step)
DEFAULT_RECIPIENT=$(lcurs-admin show | jq -r .recipient)
lcurs-admin account add alice --key "$DEFAULT_RECIPIENT"

# 6. Enable TOTP for the account – you will get a secret and an otpauth:// URI
lcurs-admin account otp enable alice
# Save the secret and URI, then add it to an authenticator app (e.g., Google Authenticator)

# 7. Generate an API token for alice
lcurs-admin auth generate-token alice
# The output contains a 64‑character hex token – keep it secure.

# 8. Verify authentication using the token and a current OTP code
OTP_CODE=$(lcurs-admin auth code alice | jq -r .code)
lcurs-admin auth verify alice --token <TOKEN> --otp "$OTP_CODE"
# If successful, you receive: { "success": true, "message": "Authentication successful" }

Architecture & Data Flow

The following diagram illustrates the main components and data flow of lcurs-admin.

flowchart LR
    subgraph Commands
        direction TB
        INFO[info]
        SETUP[setup]
        INIT[init]
        SHOW[show]
        ENCRYPT[encrypt]
        DECRYPT[decrypt]
        ACCOUNT[account]
        AUTH[auth]
    end

    subgraph Internals
        ENC_SYS[encryption_system]
        DEC_SYS[decryption_system]
        ACC_MGR[account_manager]
        AUTH_SYS[authenticator_systems]
        OTP[otp_systems]
        CONFIG_SEC[config_secure]
    end

    subgraph Storage
        AGE_KEYS[age keypair]
        REGISTRY[encrypted registry]
        ENV_VARS[LCURS_MASTER_SECRET]
    end

    INIT --> AGE_KEYS
    ENCRYPT --> ENC_SYS --> AGE_KEYS
    DECRYPT --> DEC_SYS --> AGE_KEYS
    ACCOUNT --> ACC_MGR --> REGISTRY
    AUTH --> AUTH_SYS --> REGISTRY
    SETUP --> CONFIG_SEC
    REGISTRY -.-> ENV_VARS
Loading

Explanation of components

  • api::init_keys – Generates a new Age key pair and stores the private key with 0o600 permissions, public key with 0o644.
  • api::encryption_system – Encrypts a file for a list of recipients (either provided directly or resolved from registry accounts). Supports both ASCII‑armored and binary output.
  • api::decryption_system – Decrypts an Age‑encrypted file using provided identities or the default private key. Can also iterate over all registered private keys (--all-registry).
  • api::account_manager – Manages the encrypted registry. It uses ChaCha20‑Poly1305 + HMAC‑SHA256 for authenticated encryption. The encryption key is derived via HKDF from LCURS_MASTER_SECRET and a random salt stored in the registry.
  • api::authenticator_systems – Provides high‑level authentication functions: register user, generate/revoke API token, verify token+OTP, produce OTP codes.
  • api::otp_systems – Pure TOTP implementation (RFC 6238) with HMAC‑SHA1, 30‑second time step, 6 or 8 digits (configurable; default is 6 digits in the current code).
  • core::config_secure – An optional encrypted configuration file (config.bin) that can store LCURS_KEYS_DIR and LCURS_MASTER_SECRET, protected by a user‑provided password (Argon2 + ChaCha20‑Poly1305 + HMAC).
  • command/executor – Glues everything together, parses CLI arguments, and produces JSON output.

Data flow for account registry encryption

  1. On first use (e.g., account add), a random 32‑byte salt is generated.
  2. LCURS_MASTER_SECRET is read from the environment (or from config.bin if setup was run).
  3. HKDF‑SHA256 expands the master secret together with the salt to produce two 32‑byte keys: enc_key and mac_key.
  4. The registry payload (JSON) is encrypted with enc_key using ChaCha20‑Poly1305, prefixed with the salt and a random nonce.
  5. An HMAC‑SHA256 tag is computed over the entire encrypted blob using mac_key and appended.
  6. The resulting binary blob is written atomically to meta.bin (and a backup copy).

On subsequent loads, the process is reversed: read blob, verify HMAC, decrypt, and deserialize.


Command Reference

Global Options

These options can be used with any command and must appear before the subcommand.

Option Description
-v, --verbose Increase logging verbosity (can be repeated: -v = debug, -vv = trace).
-q, --quiet Suppress all logging except errors.
--json Force JSON output (already default, kept for compatibility).
--output <human|json> Output format; default human (pretty JSON).
--log-format <pretty|json|compact> Format of log messages (stderr).
--no-color Disable coloured log output.
-h, --help Show help.
-V, --version Show version.

Example:

lcurs-admin -v --output json account list

Informational Commands

info

Displays build and package information.

lcurs-admin info

Example output:

{
	"package": {
		"name": "lcurs-administrator",
		"version": "0.1.0",
		"description": "Administrator tools for LCURS package",
		"repository": "https://github.com/neuxdotdev/lcurs-administrator",
		"license": "MIT"
	},
	"build": {
		"time": "2025-04-21 20:00:00 +07:00",
		"profile": "release",
		"target": "x86_64-unknown-linux-gnu",
		"rustc": "1.85.0",
		"build_id": "a1b2c3d4e5f67890",
		"features": []
	}
}

Key Management

init

Generates a new Age X25519 key pair. The private key is saved as privateKeys.asc (permission 0o600), and the public key as publicKeys.pub.asc (permission 0o644) inside the keys directory. If keys already exist, you are prompted before overwriting.

lcurs-admin init

Output:

{
	"status": "success",
	"recipient": "age1...",
	"paths": {
		"secret": "/home/user/.config/lcurs/lcurs-administrator/privateKeys.asc",
		"public": "/home/user/.config/lcurs/lcurs-administrator/publicKeys.pub.asc"
	}
}

show

Prints the default recipient (public key) to stdout.

lcurs-admin show

Output:

{
	"recipient": "age1qxxc3we3t75mgfjj4qf3qha7qnscepm2acfk2gmrlqwu8epm0ahs3d4qey"
}

File Encryption & Decryption

encrypt

Encrypts a file for one or more recipients.

Syntax:

lcurs-admin encrypt -i <INPUT> -o <OUTPUT> [OPTIONS]

Options:

Option Description
-i, --input <FILE> Input file (required).
-o, --output <FILE> Output encrypted file (required).
-r, --recipient <KEY or FILE> Age public key (starts with age1) or a path to a file containing the key. Can be repeated.
--to <NAME> Use the public key of an existing account from the registry. Can be repeated.
--armor Produce ASCII‑armored output (default: true). Use --armor false for binary.
-f, --force Overwrite output file without confirmation.

If neither --recipient nor --to is provided, the default recipient (from show) is used.

Examples:

# Encrypt using default recipient
lcurs-admin encrypt -i doc.pdf -o doc.pdf.age

# Encrypt for two explicit recipients
lcurs-admin encrypt -i secret.txt -o secret.age -r age1... -r age2...

# Encrypt for accounts alice and bob (must already exist in registry)
lcurs-admin encrypt -i data.bin -o data.age --to alice --to bob

# Binary mode (no ASCII armor)
lcurs-admin encrypt -i file -o file.age --armor false

Output:

{
	"status": "success",
	"input": "secret.txt",
	"output": "secret.age",
	"mode": "Armored",
	"bytes_processed": 12345,
	"recipient_count": 2
}

decrypt

Decrypts an Age‑encrypted file.

Syntax:

lcurs-admin decrypt -i <INPUT> -o <OUTPUT> [OPTIONS]

Options:

Option Description
-i, --input <FILE> Encrypted input file (required).
-o, --output <FILE> Decrypted output file (required).
-i, --identity <FILE> Path to an Age private key file (can be repeated).
--all-registry Attempt decryption using every private key linked to accounts in the registry.
-f, --force Overwrite output file without confirmation.

If no identity is provided and --all-registry is not used, the default private key (privateKeys.asc) is used.

Examples:

# Decrypt using default private key
lcurs-admin decrypt -i secret.age -o secret.txt

# Decrypt using a specific private key file
lcurs-admin decrypt -i secret.age -o out.txt --identity bob.key

# Decrypt by trying all registry-linked keys
lcurs-admin decrypt -i shared.age -o shared.txt --all-registry

Output:

{
	"status": "success",
	"input": "secret.age",
	"output": "secret.txt",
	"decrypted_bytes": 12345
}

Account Registry

All account commands operate on an encrypted registry stored in $LCURS_KEYS_DIR/.registry/meta.bin. The registry requires LCURS_MASTER_SECRET to be set.

account list

Lists all registered accounts with their metadata and active status.

lcurs-admin account list

Output:

{
	"accounts": [
		{
			"name": "alice",
			"public_key": "age1...",
			"private_key_path": "/home/user/.config/lcurs/keys/alice.key",
			"metadata": { "role": "admin", "email": "alice@example.com" },
			"created_at": 1713715200,
			"last_used": 1713715200,
			"active": true
		},
		{
			"name": "bob",
			"public_key": "age1...",
			"private_key_path": null,
			"metadata": { "role": "user" },
			"created_at": 1713715300,
			"last_used": 1713715300,
			"active": false
		}
	],
	"count": 2
}

account add

Creates a new account.

Syntax:

lcurs-admin account add <NAME> --key <KEY> [--private-key <FILE>] [--meta KEY=VALUE]...
  • <NAME>: unique account name.
  • -k, --key: Age public key (must start with age1).
  • --private-key: optional path to the associated private key file.
  • --meta: arbitrary metadata (can be repeated).

Example:

lcurs-admin account add alice --key age1... --private-key alice.asc --meta role=admin --meta "department=IT"

Output:

{ "status": "success", "name": "alice" }

account remove

Removes an account from the registry. Prompts for confirmation unless -f is given.

lcurs-admin account remove alice -f

account use

Sets the active account. The active account is used as a default when --to is omitted in encrypt (if the encrypt command uses registry‑based recipients) or when --all-registry is used in decrypt (it will always try all keys, but active does not affect that). Currently, the active account is mostly informative.

lcurs-admin account use alice

account active

Displays the currently active account name, or null if none.

lcurs-admin account active

Output:

{ "active_account": "alice" }

account import-private

Links a private key file to an existing account.

lcurs-admin account import-private alice --key-file /path/to/alice.key

account otp

Subcommands for managing TOTP on an account.

  • enable – Generates a new TOTP secret, stores it in the registry, and outputs the secret and otpauth:// URI. Use the URI to add the account to an authenticator app.
  • disable – Removes the OTP secret from the account.
  • show – Displays the current secret and URI (without modifying anything).
  • verify – Checks a user‑provided OTP code against the current time window (with one step drift tolerance).

Examples:

lcurs-admin account otp enable alice
lcurs-admin account otp show alice
lcurs-admin account otp verify alice --code 123456
lcurs-admin account otp disable alice

Enable output:

{
	"status": "success",
	"name": "alice",
	"secret": "JBSWY3DPEHPK3PXP",
	"uri": "otpauth://totp/LCURS:alice?secret=JBSWY3DPEHPK3PXP&issuer=LCURS&digits=6"
}

Authentication & API Tokens

The auth family of commands provides a higher‑level authentication interface intended for application use (e.g., an API server). It reuses the same registry as account.

auth register

Registers a new user (creates an account). Requires a public key (must start with age1). Metadata can be added.

lcurs-admin auth register alice --public-key age1... --meta "source=cli"

Output:

{
	"status": "success",
	"user": {
		"name": "alice",
		"public_key": "age1...",
		"metadata": { "source": "cli" },
		"created_at": 1713715400,
		"otp_enabled": false,
		"api_token_exists": false
	}
}

auth generate-token

Generates a new API token for a user (random 32 bytes → hex). The token is stored in the registry.

lcurs-admin auth generate-token alice

Output:

{ "token": "a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef12345678" }

auth show-token

Displays the current token for a user (if any).

lcurs-admin auth show-token alice

auth revoke-token

Removes the token from the user’s account.

lcurs-admin auth revoke-token alice

auth verify

Verifies a user’s API token and a current OTP code. Returns success/failure.

lcurs-admin auth verify alice --token <TOKEN> --otp <CODE>

Output (success):

{ "success": true, "message": "Authentication successful" }

Output (failure):

{ "success": false, "message": "Invalid OTP code" }

auth code

Generates a TOTP code for the current time (without verification). Useful for testing or for the client to obtain the code before sending to auth verify.

lcurs-admin auth code alice

Output:

{ "code": "123456" }

With --watch, it continuously updates the code every second (this mode cannot be used with --json because it prints live updates to the console).

lcurs-admin auth code alice --watch

auth list-users

Lists all registered users (same as account list but under the auth command).

lcurs-admin auth list-users

Environment Variables

Variable Required Description
LCURS_MASTER_SECRET Yes (for registry) 32‑byte key used to derive the registry encryption keys. Can be hex (64 characters) or base64. Generate with openssl rand -hex 32.
LCURS_KEYS_DIR No Absolute path to the directory where keys and the registry are stored. Default: $HOME/.config/lcurs/lcurs-administrator.
LCURS_NO_PROGRESS No If set to any value, disables the progress bar (useful for CI environments).
RUST_LOG No Overrides the log level (e.g., RUST_LOG=debug). Takes precedence over -v/-q.

Note: The LCURS_MASTER_SECRET is critical for the security of the registry. Keep it secret and never commit it to version control. You can store it in a .env file or use the setup command to save it in an encrypted config.bin.


Security Model

lcurs-administrator employs multiple layers of security to protect sensitive data.

Key Storage

  • Default key directory permissions: 0o700 (drwx------).
  • Private key files: 0o600 (-rw-------).
  • Public key files: 0o644 (-rw-r--r--).
  • On non‑Unix platforms, permissions are not enforced; a warning is logged.

Registry Encryption

  • Algorithm: ChaCha20‑Poly1305 (authenticated encryption) + HMAC‑SHA256.
  • Key derivation: HKDF‑SHA256 using LCURS_MASTER_SECRET as input keying material (IKM) and a random 32‑byte salt stored in the registry header. Two output keys are produced: one for encryption, one for MAC.
  • Nonce: Random 12‑byte nonce generated for each encryption operation.
  • Integrity: An HMAC‑SHA256 tag over the entire encrypted blob (including salt and nonce) ensures tamper detection.
  • Atomic writes: All writes go to a temporary file first, then rename() to the target, preventing partial writes or corruption.
  • Automatic backup: A copy meta.bin.bak is kept.

Master Secret Handling

  • The LCURS_MASTER_SECRET is never stored on disk in plaintext (unless you choose to write it in an environment file). It is loaded into memory as a Zeroizing buffer, which zeroes the memory on drop.
  • It can be supplied via environment variable or via the encrypted config.bin (which itself is protected by a user password).

Secure Configuration (config.bin)

  • Encrypted using Argon2 (key derivation) + ChaCha20‑Poly1305 + HMAC.
  • The user provides a password each time setup is run; the password is not stored.
  • This file can safely store LCURS_KEYS_DIR and LCURS_MASTER_SECRET so that you don’t need to export them in your shell.

Path Validation

When linking a private key file to an account, the path is canonicalized and checked to be inside the keys directory. Directory traversal (e.g., ../) is rejected.

Zeroization

Sensitive data structures (SecretString, Zeroizing arrays) are used for cryptographic keys, passwords, and secrets. They automatically overwrite their memory when dropped, reducing the risk of exposure in core dumps or memory analysis.

Logging

Logs (stderr) may contain debug information when -v is used, but they never include private keys or full secrets. OTP secrets are not logged.


API Reference for Library Users

While the primary use of this crate is the binary, many internal modules are public and can be used as a library. Add lcurs-administrator as a dependency in your Cargo.toml to leverage the following APIs.

api::init_keys

  • generate_keypair() -> (SecretString, String) – returns a new Age private key (as a SecretString) and its public key string.
  • init_keys() -> Result<(SecretString, String), io::Error> – generates a key pair and saves it to the default location.

api::encryption_system

  • encrypt_file<F>(input_path, output_path, recipients, mode, progress_cb) -> Result<(), HandlerError> – encrypts a file. recipients is a slice of public key strings. mode can be ArmorMode::Armored or Binary. progress_cb is an optional closure receiving (current, total).
  • ArmorMode enum.

api::decryption_system

  • decrypt_file<F>(input_path, output_path, identities, progress_cb) -> Result<(), HandlerError> – decrypts a file. identities is a slice of age::x25519::Identity. If empty, the default identity is used.
  • load_identity_from_file(path) -> Result<X25519Identity, HandlerError> – loads an Age private key from a file.

api::account_manager

  • SecureRegistry struct – provides low‑level access to the encrypted registry.
    • load() -> Result<Self, HandlerError>
    • load_or_init() -> Result<Self, HandlerError>
    • save(&mut self) -> Result<(), HandlerError>
    • add_account(...), remove_account(...), set_active(...), list_accounts(), etc.
  • High‑level functions: add_account, remove_account, use_account, list_accounts_json, enable_account_otp, verify_account_otp, disable_account_otp, get_account_otp_secret, generate_api_token, etc.

api::otp_systems

  • generate_secret() -> String – returns a Base32‑encoded secret (20 bytes).
  • totp(secret: &str, timestamp: u64) -> Result<String, OtpError> – computes the TOTP code for the given UNIX timestamp (seconds).
  • verify(secret: &str, code: &str) -> Result<bool, OtpError> – verifies a user code against current time (±1 time step).
  • generate_otp_uri(account_name, secret, issuer) -> String – returns an otpauth:// URI.

api::authenticator_systems

  • register_user(name, public_key, metadata) -> Result<RegisteredUser, HandlerError>
  • generate_api_token_for_user(name) -> Result<String, HandlerError>
  • verify_user_auth(name, token, otp_code) -> Result<AuthResult, HandlerError>
  • generate_current_otp_code(name) -> Result<String, HandlerError>
  • watch_otp_code(name, callback) -> JoinHandle<()>

Error Handling

All public functions return a Result<T, HandlerError>, where HandlerError is a comprehensive error enum (IO, key generation, validation, config, etc.). The WithContext trait allows adding context to errors.


Development Guide

Prerequisites

  • Rust 1.70+
  • Cargo
  • (Optional) git for embedding commit info via built crate.

Build

cargo build --release

The binary will be in target/release/lcurs-admin.

Testing

cargo test

Integration tests are in the tests/ directory (if any). The existing unit tests are inside each module.

Build Script

build.rs uses the built crate to collect metadata (version, dependencies, Git commit, etc.) and exposes them via built.rs which is included in src/core/info.rs. If you are building in an environment without Git (e.g., a released source tarball), the Git commit will be omitted.

Code Structure

  • src/api/ – core cryptographic and registry operations.
  • src/command/ – CLI parsing and command execution.
  • src/core/ – configuration, prompting, build info.
  • src/handler/ – error types, logging setup.

Adding a New Command

  1. Define a new variant in Command enum in src/command/types.rs.
  2. Add the corresponding parsing logic (if needed) using clap attributes.
  3. Implement a handler function (e.g., handle_mycmd) in src/command/executor.rs that returns CommandResult (a Result<serde_json::Value, HandlerError>).
  4. Match the new variant in the execute method.
  5. Ensure the handler returns a JSON value (the final output) and does not print anything directly (unless it is a watch mode or interactive). The outer execute will print the JSON.

Logging

Use the tracing macros (info!, debug!, warn!, error!) for logging. Logs go to stderr and are formatted according to --log-format. Do not use println! for debugging; it would interfere with JSON output. Use eprintln! only for interactive prompts or watch mode.

Progress Bars

Progress bars are provided by the indicatif crate. They are automatically disabled when LCURS_NO_PROGRESS is set. To add a progress bar in a new command, follow the pattern in encrypt_with_progress.

Security Considerations When Contributing

  • Always use Zeroizing for secrets.
  • Never log secrets or full OTP secrets.
  • Validate file paths to prevent directory traversal.
  • Use atomic write patterns for any file that could be partially written.
  • Keep the registry encryption scheme auditable.

License

This project is licensed under the MIT License. See the LICENSE file for details.


Maintained by neuxdotdev.
For issues, feature requests, or contributions, please use the GitHub issue tracker.

About

cli tools for lcurs ecosystems

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

 
 
 

Contributors