What is NSFS
nsfs (NSFS) is a single-Go-binary self-hosted file manager: browse the local filesystem and remote SSH/SFTP servers from the browser, with JWT login, groups→roles→permissions RBAC (scoped per connection and path prefix), an audit log, and nslib licensing with a 30-day trial.
- Local + remote: the sidebar shows the
Localroot plus each SSH connection as an extra root; navigation works the same on both. - No frontend build: Alpine.js + Tailwind via CDN, 6 languages (EN is the source of truth, ES fully translated, FR/DE/IT/PT fall back to EN).
- SSH credentials encrypted at rest with AES-256-GCM (
security.aes_key).
Host names, paths, and users in the examples are fictional.
Use cases
1. Browse and operate on the allowed local directory
List /data, create folders, upload/download, rename, and delete.
nsfs init ./mydata
nsfs start --config ./mydata
nsfs status
Web flow: login → Local connection in the sidebar → navigate with breadcrumbs → list/grid view → new folder, upload (drag & drop), download, rename, delete, context menu.
2. Add a remote server over SSH and browse it
Register the prod-example host and test the connection before using it.
Web flow: Administration → connections → create (host, port 22, user, password or key auth with optional passphrase, root_path) → test-connection button → it appears as a sidebar root (green dot = connected, grey = idle, red = error) → browse just like local.
3. Give a team read-only access to one remote path
The operators group may only list /var/log on host prod-example.
Web flow: Administration → access rules → create a rule (connection_id + path_prefix + list|read actions) for the group → verify with the per-user effective view → the user sees a filtered listing and gets 403 when trying to create folders outside their jail.
4. Bulk upload, search, and download as zip with auditing
Upload a batch, find a file by name, and download the selection as an archive.
Web flow: multi-select files → bulk actions (bulk delete/move) → download as zip → search by name (case-insensitive, paginated with ?page=&limit=) → every operation is recorded in audit/YYYY-MM-DD.jsonl and the recent-audit view.
Interface

Reference
CLI commands
| Command | Description |
|---|---|
nsfs init [dir] [--admin-password PASS] | Creates the environment: config.yaml + SQLite DB + folders. Without --admin-password a random 6-char password is generated |
nsfs start [dir] / --config dir | Starts the server in the background (daemon) |
nsfs stop [--config dir] | Stops the daemon |
nsfs status | PID, uptime, memory |
nsfs run [--config dir] | Foreground (internal, not listed in --help) |
nsfs --version | Version and build |
Main configuration (config.yaml)
| Key | Description |
|---|---|
server.host / server.port | Listen address (default 0.0.0.0:3333) |
server.tls_cert_file / tls_key_file | Optional TLS |
database.path | SQLite path (./db/security.db) |
security.jwt_secret | JWT secret (min. 32 chars) |
security.token_expiry_hours | Token lifetime (24) |
security.aes_key | SSH credential encryption key |
security.password_reset_code_ttl_minutes | Reset-code lifetime (10) |
fs.local_roots | Local jail ([/data, /tmp]; empty = no restriction) |
fs.max_upload_mb | Max upload size (500) |
fs.hidden_patterns | Hidden patterns |
fs.audit_dir | Audit directory (./audit) |
smtp.* | Optional SMTP for reset emails |
pagination.page_size | Page size (100) |
Web UI areas
File browser (breadcrumbs, list/grid, search, sort by name/size/date, multi-select, context menu, drag & drop) · preview (text/code, image, PDF; ~1 MB cap) and inline editor with save · SSH connections · bookmarks · administration (users, groups, roles, access rules, per-user effective view, my-permissions) · recent audit · config editor · about · themes and language switcher.
Commented examples
# 1. New environment and start
nsfs init ./mydata
nsfs start --config ./mydata
nsfs status
# 2. Pin the admin password at init (for scripting)
nsfs init /opt/nsfs-prod --admin-password "strong-secret"
# 3. Show version
nsfs --version
# 4. Stop the daemon
nsfs stop --config ./mydata
# Paginated listing via API (with JWT token)
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:3333/api/fs/list?connection=local&path=/data&page=1&limit=100"
# Search by name
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:3333/api/fs/search?connection=local&path=/data&q=report"
FAQ
Where is the admin password set? At init: without --admin-password a random one is generated (shown once); pin it with --admin-password for automation.
Why do I get 403 creating a folder? The listing is filtered by your permissions (list|read), and writing requires a rule on that connection_id + path_prefix. Check the user’s effective view.
Are SSH passwords stored in clear text? No: they are encrypted with AES-256-GCM using security.aes_key and never logged.
Which languages are supported? EN (source), ES (complete), FR/DE/IT/PT (fall back to EN for any missing key).