
Company News
Socket Named Top Sales Organization by RepVue
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.
cabinet
Advanced tools
Cabinet is a lightweight, flexible data organization tool that lets you manage your data with the simplicity of a JSON file or the power of MongoDB - your choice.
prompt_toolkitmongodb_connection_string replaces mongodb_username and mongodb_password.Install pipx if you don't have it already
Install cabinet:
pipx install cabinet
cabinet --config
curl -s https://api.github.com/repos/tylerjwoodfin/cabinet/releases/latest \
| grep "browser_download_url" \
| cut -d '"' -f 4 \
| xargs curl -L -o cabinet.pex
sudo mv cabinet.pex /usr/local/bin/cabinet
Outside of the standard Python library, the following packages are included as part of pipx install cabinet:
pymongo: Provides the MongoDB client and related errors.prompt_toolkit: Provides functionality for command-line interfaces.~/.cabinet/data.json or MongoDB
~/.cabinet/log/LOG_DAILY_YYYY-MM-DD by default
~/.cabinet/log as needed by setting/modifying ~/.config/cabinet/config.json -> path_dir_logUsage: cabinet [OPTIONS]
Options:
-h, --help show this help message and exit
--configure, -config Configure
--edit, -e Edit Cabinet as MongoDB as a JSON file
--edit-file EDIT_FILE, -ef EDIT_FILE
Edit a specific file
--force-cache-update Disable using the cache for MongoDB queries
--no-create (for -ef) Do not create file if it does not exist
--get GET [GET ...], -g GET [GET ...]
Get a property from MongoDB
--put PUT [PUT ...], -p PUT [PUT ...]
Put a property into MongoDB
--append APPEND [APPEND ...], -a APPEND [APPEND ...]
Append to a string or array
--remove REMOVE [REMOVE ...], -rm REMOVE [REMOVE ...]
Remove a property from MongoDB
--get-file GET_FILE Get file
--export Exports MongoDB to ~/.cabinet/export
--strip (for --get-file) Whether to strip file content whitespace
--log LOG, -l LOG Log a message to the default location
--level LOG_LEVEL (for -l) Log level [debug, info, warn, error, critical]
--tags LOG_TAGS (for -l) Comma-separated list of tags to associate with the log entry
--query [LOG_QUERY_FILE], -q [LOG_QUERY_FILE]
Query log files (optional: specify log file name, defaults to today)
--query-tags QUERY_TAGS
(for --query) Comma-separated list of tags to filter by
--query-path QUERY_PATH
(for --query) Filter by file path (fuzzy search)
--query-hostname QUERY_HOSTNAME
(for --query) Filter by hostname
--query-level QUERY_LEVEL
(for --query) Filter by log level [debug, info, warning, error, critical]
--query-date QUERY_DATE
(for --query) Filter by date (YYYY-MM-DD format)
--query-message QUERY_MESSAGE
(for --query) Search within message text
--editor EDITOR (for --edit and --edit-file) Specify an editor to use
-v, --version Show version number and exit
Mail:
--mail Sends an email
--subject SUBJECT, -s SUBJECT
Email subject
--body BODY, -b BODY Email body
--to TO_ADDR, -t TO_ADDR
The "to" email address
Configuration data is stored in ~/.config/cabinet/config.json.
Upon first launch, the tool will walk you through each option.
path_dir_log is the directory where logs will be stored by default.mongodb_enabled is a boolean that determines whether MongoDB is used.mongodb_db_name is the name of the database you want to use by default.mongodb_connection_string is the connection string for MongoDB.editor is the default editor that will be used when editing files.~/.cabinet/data.json.Follow these instructions to find your MongoDB connection string: MongoDB Atlas or MongoDB (for local MongoDB, untested).
You will be asked to configure your default editor from the list of available editors on
your system. If this step is skipped, or an error occurs, nano will be used.
You can change this with cabinet --config and modifying the editor attribute.
Your config.json should look something like this:
{
"path_dir_log": "/path/to/your/log/directory",
"mongodb_db_name": "cabinet (or other name of your choice)",
"editor": "nvim",
"mongodb_enabled": true,
"mongodb_connection_string": "<your connection string>",
}
cabinet -ef shopping from the terminal
cabinet -ef "~/path/to/shopping_list.md"cabinet.Cabinet().edit("shopping")
cabinet.Cabinet().edit("~/path/to/whatever.md")file:
# example only; these commands will be unique to your setup
{
"path": {
"edit": {
"shopping": {
"value": "~/path/to/whatever.md",
},
"todo": {
"value": "~/path/to/whatever.md",
}
}
}
}
set from terminal:
cabinet -p edit shopping value "~/path/to/whatever.md"
cabinet -p edit todo value "~/path/to/whatever.md"
smtplib.cabinet -e), add the email object to make your settings file look like this example:file:
{
"email": {
"from": "throwaway@example.com",
"from_pw": "example",
"from_name": "Cabinet (or other name of your choice)",
"to": "destination@protonmail.com",
"smtp_server": "example.com",
"imap_server": "example.com",
"port": 123
}
}
set from terminal:
cabinet -p email from throwaway@example.com
cabinet -p email from_pw example
...
putpython:
from cabinet import Cabinet
cab = Cabinet()
cab.put("employee", "Tyler", "salary", 7.25)
or terminal:
cabinet -p employee Tyler salary 7.25
results in this structure in MongoDB:
{
"employee": {
"Tyler": {
"salary": 7.25 # or "7.25" if done from terminal
}
}
}
getpython:
from cabinet import Cabinet
cab = Cabinet()
print(cab.get("employee", "Tyler", "salary"))
# or cab.get("employee", "Tyler", "salary", is_print = True)
or terminal:
cabinet -g employee Tyler salary
--force-cache-update to force a cache updateresults in:
7.25
removepython:
from cabinet import Cabinet
cab = Cabinet()
cab.remove("employee", "Tyler", "salary")
or terminal:
cabinet -rm employee Tyler salary
results in this structure in MongoDB:
{
"employee": {
"tyler": {}
}
}
appendAppends a value to an existing string (concatenation) or array. For other types (bool, int, float, etc.), prints a graceful error. Prints the updated value.
python:
from cabinet import Cabinet
cab = Cabinet()
# Append to array: ['apple'] -> ['apple', 'banana']
cab.put("fruits", ["apple"])
cab.append("fruits", "banana", is_print=True)
# Append to string: 'apple' -> 'applebanana'
cab.put("fruits", "apple")
cab.append("fruits", "banana", is_print=True)
or terminal:
# Append to array: ['apple'] -> ['apple', 'banana']
cabinet -p fruits '["apple"]'
cabinet -a fruits banana
# Append to string: 'apple' -> 'applebanana'
cabinet -p fruits apple
cabinet -a fruits banana
# Nested paths
cabinet -a person tyler fruits banana
['apple'] → ['apple', 'banana']'apple' → 'applebanana'editterminal:
# opens file in the default editor (`cabinet --config` -> 'editor'), saves upon exit
cabinet -e
# or
cabinet --edit
# you can add an 'editor':
cabinet -e --editor=code
edit_filepython:
from cabinet import Cabinet
cab = Cabinet()
# if put("path", "edit", "shopping", "/path/to/shopping.md") has been called, this will edit the file assigned to that shortcut.
# opens file in the default editor (`cabinet --config` -> 'editor'), saves upon exit
cab.edit("shopping")
# or you can edit a file directly...
cab.edit("/path/to/shopping.md")
# you can pass an 'editor' to override the default:
cab.edit("/path/to/shopping.md", editor="nvim")
terminal:
# assumes path -> edit -> shopping -> path/to/shopping.md has been set
cabinet -ef shoppping
# or
cabinet -ef "/path/to/shopping.md"
# or
mailpython:
from cabinet import Mail
mail = Mail()
mail.send('Test Subject', 'Test Body')
terminal:
cabinet --mail --subject "Test Subject" --body "Test Body"
# or
cabinet --mail -s "Test Subject" -b "Test Body"
logpython:
from cabinet import Cabinet
cab = Cabinet()
# writes to a file named LOG_DAILY_YYYY-MM-DD in `~/.cabinet/log` inside a YYYY-MM-DD folder
# writes somewhere other than `~/.cabinet/log`, if `~/.config/cabinet/config.json` has `path_dir_log` set
cab.log("Connection timed out") # defaults to 'info' if no level is set
cab.log("This function hit a breakpoint", level="debug")
cab.log("Looks like the server is on fire", level="critical")
cab.log("This is fine", level="info")
# NEW: Log with tags (optional list of strings)
cab.log("Checked weather successfully", tags=["weather"])
cab.log("Starting Borg Backup...", tags=["backup", "start"])
cab.log("Pruning repository", tags=["backup", "prune"])
cab.log("Compacting repository", tags=["backup", "compact"])
# writes to a file named LOG_TEMPERATURE in the default log directory
cab.log("30", log_name="LOG_TEMPERATURE")
# writes to a file named LOG_TEMPERATURE in ~/weather
cab.log("30", log_name="LOG_TEMPERATURE", log_folder_path="~/weather")
# format (without tags)
# 2025-10-28 17:01:01,858 — INFO -> tools/weather.py:34@{hostname} -> Checking weather
# format (with tags)
# 2025-09-27 02:01:09,012 — INFO [weather] -> tools/weather.py:116@cloud -> Checked weather successfully
# 2025-09-27 03:05:03,732 — INFO [backup,start] -> bin/cabinet:8@cloud -> Starting Borg Backup...
terminal:
# defaults to 'info' if no level is set
cabinet -l "Connection timed out"
# -l and --log are interchangeable
cabinet --log "Connection timed out"
# change levels with --level
cabinet --log "Server is on fire" --level "critical"
# add tags with --tags (comma-separated)
cabinet --log "Checked weather successfully" --tags "weather"
cabinet --log "Starting Borg Backup..." --tags "backup,start"
cabinet --log "Pruning repository" --level "info" --tags "backup,prune"
log_queryQuery log files by various criteria including tags, path, hostname, level, date, and message content.
python:
from cabinet import Cabinet
cab = Cabinet()
# Query today's log file by tag (log_file is optional and defaults to today)
results = cab.log_query(tags=["weather"])
# Returns: ['2025-10-28 17:01:09,012 — INFO [weather] -> tools/weather.py:116@cloud -> Checked weather successfully']
# Query by multiple tags in today's log (returns logs with any of these tags)
results = cab.log_query(tags=["backup"])
# Returns all logs with 'backup' tag from today
# Query a specific date's log file
results = cab.log_query("LOG_DAILY_2025-09-27.log", tags=["weather"])
# Query by log level (in today's log)
results = cab.log_query(level="ERROR")
# Query by message content (case-insensitive fuzzy search)
results = cab.log_query(message="repository")
# Query by path (case-insensitive fuzzy search on file path after arrow)
results = cab.log_query(path="cabinet")
# Query by hostname
results = cab.log_query(hostname="cloud")
# Query by date (filters by timestamp in the log entry)
results = cab.log_query(date_filter="2025-09-27")
# Combine multiple filters on today's log
results = cab.log_query(
tags=["backup"],
level="INFO",
message="repository"
)
# Combine multiple filters on specific log file
results = cab.log_query(
"LOG_DAILY_2025-09-27.log",
tags=["backup"],
level="INFO",
message="repository"
)
terminal:
# Query today's log (defaults to today if no log file specified)
cabinet --query --query-tags "weather"
# Short form
cabinet -q --query-tags "backup"
# Query by level
cabinet --query --query-level "ERROR"
# Query by message content
cabinet --query --query-message "repository"
# Query by path (fuzzy search)
cabinet --query --query-path "tools"
# Query by hostname
cabinet --query --query-hostname "cloud"
# Query by date
cabinet --query --query-date "2025-10-28"
# Combine multiple filters
cabinet --query --query-tags "backup" --query-level "INFO"
# Query specific log file
cabinet --query "LOG_DAILY_2025-09-27.log" --query-tags "weather"
# Complex query with multiple filters
cabinet -q "LOG_DAILY_2025-09-27.log" --query-tags "backup,weather" --query-level "INFO" --query-message "repository"
logdbpython:
from cabinet import Cabinet
cab = Cabinet()
cab.logdb("Connection timed out") # logs default to a `logs` collection in MongoDB
cab.logdb("This function hit a breakpoint", level="debug", collection_name="debugging logs") # customize the collection name
cab.logdb("Temperature changed significantly", level="critical", db_name="weather") # customize the database name
cab.logdb("This is fine", level="info", cluster_name="myCluster") # customize the cluster name
terminal:
# defaults to 'info' if no level is set
cabinet -ldb "Connection timed out"
# -l and --log are interchangeable
cabinet --logdb "Connection timed out"
# change levels with --level
cabinet --logdb "Server is on fire" --level "critical"
The cabinet.ui module provides interactive command-line interface components:
from cabinet.ui import list_selection, render_html, confirmation
# List selection
items = ["Option 1", "Option 2", "Option 3"]
selected_index = list_selection(items, "Choose an option:")
# HTML rendering
render_html("<b>Bold text</b> and <i>italic text</i>")
# Confirmation dialog
result = confirmation("Do you want to proceed?", "Confirmation")
FAQs
Easily manage data storage and logging across repos
We found that cabinet demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.