ByteStash 1.6.0
Overview
This guide covers launching and verifying ByteStash 1.6.0 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened Node.js & Developer and API Tools AMIs listing on AWS Marketplace. You pick it as the software version when you subscribe and launch. The listing's guide hub describes the other products in the listing.
ByteStash is a self-hosted code-snippet manager. You store reusable snippets, each with one or more files, a description in Markdown and categories. You find them again with full-text search, syntax highlighting (the Monaco editor) and Markdown and Mermaid rendering. Snippets are private to their owner unless the owner marks one public or creates a share link.
Every account can create API keys for the REST API and for ByteStash's remote MCP endpoint, so AI assistants such as Claude can search, read and save snippets.
The image runs ByteStash 1.6.0, built by SecureAnyCloud from the official 1.6.0 source with Node.js 22.23.4. ByteStash 1.6.0 and the releases before it fixed three security problems that earlier images carried. Version 1.5.14 stops a signed-in user from overwriting another user's snippet. Version 1.6.0 stops the public API from serving recycled (deleted) public snippets, and marks the session cookie Secure behind HTTPS.
The build also updates the libraries that npm audit flags in the production tree, so the server has no known advisory (details below). The base is a fully patched Ubuntu 24.04 LTS, and unattended security upgrades stay on for your instance.
What you get:
- ByteStash 1.6.0 (
bytestash.service) running as the unprivilegedbytestashuser on127.0.0.1:5000only, with its SQLite database in/var/lib/bytestash - nginx publishing ByteStash over HTTPS on port 443 with a certificate created on your instance's first boot; port 80 redirects to HTTPS
- One
administrator account, admin, with a password created on your instance's first boot, and a JSON Web Token (JWT) signing secret unique to your instance bytestash-set-hostname, a helper to serve ByteStash under your own DNS name, andsac-bytestash-probe, a sign-in round-trip test- The build record (
BUILD-INFO.txt), thenpm auditresults, the licences and the complete ByteStash 1.6.0 source archive in/usr/share/doc/sac-bytestash/
Default user: ubuntu · Architecture: x86_64 · Root volume: 8 GiB gp3
Security posture
- No open sign-up and no first-visitor race. A fresh ByteStash install lets anybody who reaches it register the first account. On this image, first boot creates the database and the
adminaccount before ByteStash is allowed to start for the first time, so registration is already closed when the instance first answers. Sign-up stays closed (ALLOW_NEW_ACCOUNTS=false), anonymous mode is off, andadminis the only account until you add more (see Maintenance). A check before every start refuses to run ByteStash or nginx until first boot has finished. - Per-instance secrets. The
adminpassword, the JWT signing secret that protects every session and API sign-in, and the TLS key are generated on your instance's first boot. No database, password, secret or key ships in the image. The password is in/root/bytestash-credentials.txt(mode 0600, root only). ByteStash stores passwords only as bcrypt hashes. - Loopback only. ByteStash listens on
127.0.0.1. nginx on 443 is the only way in. - HTTPS on first boot. nginx serves ByteStash with a TLS key and self-signed certificate created on your instance (TLS 1.2 and 1.3 only). Port 80 only redirects to HTTPS. The session cookie is
Secure,HttpOnlyandSameSite=Lax. - Fixed host name. ByteStash is served under a name taken from the instance metadata at first boot (or one you set), never from the request. Requests for any other host name are dropped, and nginx refuses cross-site form posts and WebSocket upgrades. API and MCP clients, which send no browser
Originheader, are not affected. - Real client addresses. nginx passes the connecting address to ByteStash and ignores any
X-Forwarded-Forheader that a client sends. ByteStash trusts forwarded headers only from nginx on the same instance. - No metadata access. ByteStash fetches an identity provider's configuration when you turn on single sign-on, so
bytestash.serviceandnginx.serviceare blocked from reaching the instance metadata service (169.254.169.254 and fd00:ec2::254). Amazon Time Sync keeps working. - Least privilege. ByteStash runs with no Linux capabilities and a read-only system (
ProtectSystem=strict) except its own data directory. Root owns the application files. - No scheduled firmware-metadata refresh. The
fwupdfirmware updater stays installed, but its LVFS metadata refresh (fwupd-refresh.timerand.service) is masked, so an LVFS outage never marks your instance unhealthy at boot. If you use fwupd, runsudo fwupdmgr refreshyourself. - IMDSv2 required. The AMI is registered with
ImdsSupport v2.0, so instances launched from it refuse token-less (IMDSv1) metadata requests. - Per-instance identity. SSH host keys and the machine ID are generated fresh on every instance.
What anyone who can reach the instance can read
By design, ByteStash serves a few pages without signing in. Keep them in mind when you decide who may reach port 443:
- Public snippets.
/public/snippetsin the web UI and/api/public/snippetsin the API list and serve the snippets a user has explicitly marked public, including each file's raw text. Private snippets are never listed there. Since 1.6.0, neither are snippets in the recycle bin. - Share links. A share link (
/s/<id>, and its embed/embed/<id>for other web pages) shows one snippet to whoever has the link, unless the owner ticked Require authentication when creating it. Share links can expire. - API reference.
/api-docsserves ByteStash's OpenAPI documentation.
Everything else (your snippets, API keys, settings and the admin pages) needs a signed-in session or an API key.
Known advisories and how the build handles them
- Server dependencies.
npm auditon the upstream 1.6.0 lockfile lists five advisories in the server's production tree. This build updates, within their own release lines,body-parser(a denial-of-service on every JSON request),zod(used by the MCP tool definitions), andhono,fast-uriandip-address(used by the MCP library). After the changes,npm audit --omit=devreports no advisory for the server. The result ships inserver-npm-audit-production.json. - Web interface. The browser bundle is built with
dompurify3.4.17, which fixes a critical sanitiser bypass that the upstream lockfile's version has. One low advisory remains: KaTeX, which renders maths in Mermaid diagrams, can be steered by an existing prototype-pollution bug elsewhere (GHSA-238p-pmpm-9mq7); the fix needs Mermaid 12, a major release that ByteStash has not adopted yet. It runs only in the browser, never on the server. The result ships inclient-npm-audit-production.json. - Build tools. The web interface is compiled at build time; its development tools (including
vite) and the compiler used for the native modules are removed afterwards and never ship.
Prerequisites
- An AWS account with permission to launch EC2 instances
- An EC2 key pair in the Region you launch in
- A subnet that gives the instance a public IPv4 address (or private connectivity from where you will use ByteStash)
Recommended instance type: m5.large. ByteStash is a single Node.js process with SQLite storage, so a smaller burstable type also works for a small team.
Connecting to your instance
For ByteStash 1.6.0 on Ubuntu 24.04 (operating system Ubuntu 24.04 LTS), the SSH user is ubuntu.
Launch the instance
Option A: AWS Marketplace console
- Open the SecureAnyCloud Hardened Node.js & Developer and API Tools AMIs listing on AWS Marketplace and choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- Once the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose ByteStash 1.6.0 on Ubuntu 24.04, then choose your Region.
- Pick an instance type and your key pair.
m5.largeis recommended, and the software charge is $0.04 per vCPU per hour. - Security group: allow SSH (22), HTTP (80) and HTTPS (443). Restrict SSH to your own IP address, and restrict ports 80 and 443 to the people and API clients that use ByteStash.
- Choose your VPC and a subnet that gives the instance a public IP, leave the metadata defaults (the image already requires IMDSv2), then choose Launch.
Option B: AWS CLI
Run this on your own workstation. It creates a security group that allows SSH and HTTPS from your management address only, then launches the instance.
Replace the AMI ID with the one shown for your Region on the Marketplace launch page. Replace the VPC, subnet, key pair and CIDR with your own:
SG=$(aws ec2 create-security-group --group-name bytestash \
--description "ByteStash" --vpc-id vpc-0123456789abcdef0 \
--query GroupId --output text) for p in 22 80 443; do aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port "$p" --cidr <your-mgmt-cidr> done aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--security-group-ids "$SG" \
--subnet-id subnet-0123456789abcdef0 \
--associate-public-ip-address \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=bytestash}]'You do not need --metadata-options, because the AMI already requires IMDSv2.
Connect and read your credentials
Connect as ubuntu with the key pair you chose:
ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>First boot finishes within a minute of launch. It creates the certificate, the JWT signing secret, the database and the admin account, then lets ByteStash and nginx start. Read your credentials from a root-only file:
sudo cat /root/bytestash-credentials.txt# ByteStash - generated on this instance's first boot by bytestash-firstboot.service. # Unique to this instance; nothing here shipped in the image. Store it somewhere safe. BYTESTASH_URL=https://ec2-54-81-216-2.compute-1.amazonaws.com/ BYTESTASH_USERNAME=admin BYTESTASH_ADMIN_PASSWORD=<BYTESTASH_ADMIN_PASSWORD> # # Open BYTESTASH_URL and sign in as admin (self-signed certificate minted on this instance; port 80 redirects). # Sign-up is closed: nobody can register an account. After first login change this password in the user menu # (your account name, top right) -> Change password. REST API and MCP clients use an API key: user menu -> API Keys. # Change the name ByteStash is served as:
sudo bytestash-set-hostname <your-dns-name>
# Restrict 80/443 in the security group to the people and clients that need them.
# Guide: https://secureanycloud.com/stacks/bytestash-1-6-0/BYTESTASH_ADMIN_PASSWORD holds your instance's own password. Keep it somewhere safe, and change it after your first sign-in (see Maintenance).
Confirm the services are running
Expect first boot, ByteStash and nginx to be active, and ByteStash to run as its own unprivileged user. The start guard, which runs before every start of ByteStash and nginx, passes:
systemctl is-active bytestash-firstboot.service bytestash.service nginx.service ps -o user:12=,comm= -p "$(systemctl show -p MainPID --value bytestash.service)"
sudo /usr/local/sbin/bytestash-guardactive active active bytestash node GUARD_OKIf a service ever refuses to start, run the guard. It names the exact reason.
List the TCP ports that the instance listens on:
sudo ss -Hltn | awk '{print $4}' | sort -u0.0.0.0:22 0.0.0.0:443 0.0.0.0:80 127.0.0.1:5000 127.0.0.53%lo:53 127.0.0.54:53 [::]:22 [::]:443 [::]:80ByteStash (5000) listens on loopback only, and nginx publishes it on 443. The loopback port 53 entries are Ubuntu's local stub resolver (systemd-resolved).
Check the versions and the build record:
node --version grep -E '^(ByteStash|source|tag commit|audit|database)' /usr/share/doc/sac-bytestash/BUILD-INFO.txtv22.23.4 ByteStash 1.6.0 on Ubuntu 24.04 - sac build record source https://github.com/jordan-dalby/ByteStash/archive/refs/tags/v1.6.0.tar.gz source sha256 6e534ea3b82e01e043ab55d042cb1ebab78e204d63ac35f5822bfd414c96f8f6 tag commit da3a7afe93cf06c7c01d3cd09e4a2ea52aff4e6a audit server: 0 advisories; web UI bundle: katex + mermaid LOW only (GHSA-238p-pmpm-9mq7) database SQLite (/var/lib/bytestash/snippets/snippets.db); PostgreSQL mode offSign in
Browse to the BYTESTASH_URL from Step 2, for example https://ec2-54-81-216-2.compute-1.amazonaws.com/. The certificate is self-signed, so your browser warns you once. Accept it, or install your own certificate (Step 8). ByteStash shows its sign-in page, without a link to register.
Sign in as admin with BYTESTASH_ADMIN_PASSWORD. Your snippet list opens, and a new instance has none yet. The screenshot below is from an instance with a few snippets added.
The Private and Public switch under the name toggles between your own snippets and the public ones. The small vdev label next to the name is ByteStash's version tag, which upstream fills in only for its own container builds. The version is in the build record above.
The image ships a round-trip test that signs in through nginx as a browser would. It proves that the API refuses anonymous requests, and that sign-up and a wrong password are refused. It also proves that your admin password returns an administrator session that can read your snippets:
sudo /usr/local/sbin/sac-bytestash-probePROBE_OK anonymous API 401, sign-up 403, wrong password 401, admin JWT (is_admin), verify valid, authenticated API 200 at https://ec2-54-81-216-2.compute-1.amazonaws.com/You can also confirm from the command line that sign-up is closed. Port 80 only redirects to HTTPS, ByteStash reports that new accounts are not allowed, a registration attempt is refused, and the account table holds only admin. The block stops with an error if any answer is not the expected one:
H=$(sudo sed -n 's/^set $bs_host "\(.*\)";$/\1/p' /etc/nginx/sac-bytestash-host.conf) C=(--cacert /etc/ssl/sac/bytestash.crt --resolve "$H:443:127.0.0.1" --resolve "$H:80:127.0.0.1")
R=$(curl -s "${C[@]}" -o /dev/null -w '%{http_code} %{redirect_url}' "http://$H/"); echo "$R"
A=$(curl -s "${C[@]}" "https://$H/api/auth/config" | jq -c '{allowNewAccounts, hasUsers, disableAccounts}'); echo "$A"
G=$(curl -s "${C[@]}" -o /dev/null -w '%{http_code}' -X POST -H 'Content-Type: application/json' -d '{"username":"someone","password":"Some-Password-123"}' "https://$H/api/auth/register"); echo "register: $G"
U=$(sudo sqlite3 "file:/var/lib/bytestash/snippets/snippets.db?mode=ro" "SELECT username FROM users;"); echo "$U" [ "$R" = "301 https://$H/" ] && [ "$A" = '{"allowNewAccounts":false,"hasUsers":true,"disableAccounts":false}' ] && [ "$G" = 403 ] && [ "$U" = admin ]301 https://ec2-54-81-216-2.compute-1.amazonaws.com/ {"allowNewAccounts":false,"hasUsers":true,"disableAccounts":false} register: 403 adminCreate and share a snippet
Choose New snippet, give it a title, add one or more files with their language, optional categories and a Markdown description, and save. Open a snippet to read it with syntax highlighting, copy a file, or edit it.
A snippet is private to you unless you tick Make snippet public. That lists it under Public snippets for anybody who can reach the instance.
To show one snippet to someone without making it public, open its share menu and choose Create Share Link. The link can Require authentication and can expire (for example 2d).
Move to Recycle Bin keeps a snippet for 30 days before it is deleted. From 1.6.0, the public API no longer serves a recycled snippet.
Use the REST API with an API key
Create an API key from the user menu (your account name, top right), API Keys, and copy it. ByteStash shows the key only once. Scripts send it in an x-api-key header.
You can try it on your instance. Put your key in a file that only you can read first, so it never appears on a command line:
umask 077; printf 'x-api-key: %s\n' '<your-api-key>' > ~/.bytestash-key curl -s --cacert /etc/ssl/sac/bytestash.crt -H @$HOME/.bytestash-key \
"https://<your-dns-name>/api/snippets?search=python" | jq '.data[] | {id, title}'The full API is described at https://<your-dns-name>/api-docs.
Connect an AI assistant over MCP
ByteStash serves a remote MCP (Model Context Protocol) endpoint at https://<your-host>/mcp. Add it to an MCP client (Claude, ChatGPT, Perplexity or any client that supports remote MCP servers over HTTP), with your API key as the bearer token. For example, in a client configuration file:
{ "mcpServers": { "bytestash": { "url": "https://<your-dns-name>/mcp", "headers": { "Authorization": "Bearer <your-api-key>" } } } }The assistant can then list, search, read, create, update and recycle your snippets with the six ByteStash tools. The endpoint refuses requests without a valid key. Your client must trust the instance's certificate, so install your own certificate first (Step 8).
An assistant has the same access as your account. Give it a key of its own, and delete the key under API Keys in the user menu when you no longer need it.
HTTPS certificate and host name
The certificate was created on your instance's first boot for the instance's public DNS name, public IP, private IP and localhost. Only their owners can read the key, the JWT secret, the credentials file and the data directory:
sudo openssl x509 -in /etc/ssl/sac/bytestash.crt -noout -subject -ext subjectAltName
sudo stat -c '%a %U:%G %n' /etc/ssl/sac/bytestash.key /etc/bytestash/jwt-secret /root/bytestash-credentials.txt /var/lib/bytestashsubject=CN = ec2-54-81-216-2.compute-1.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-54-81-216-2.compute-1.amazonaws.com, IP Address:54.81.216.2, IP Address:172.31.81.44, IP Address:127.0.0.1, DNS:localhost 600 root:root /etc/ssl/sac/bytestash.key 640 root:bytestash /etc/bytestash/jwt-secret 600 root:root /root/bytestash-credentials.txt 750 bytestash:bytestash /var/lib/bytestashTo serve ByteStash under your own DNS name (for example snippets.example.com, pointed at this instance), run the helper. It records the name, creates a new self-signed certificate for it, updates nginx, and rewrites the URL in the credentials file:
sudo bytestash-set-hostname snippets.example.comIf the instance's public address changes after a stop and start, run the helper again.
To use a certificate from your own certificate authority, replace /etc/ssl/sac/bytestash.key (keep it 0600 root) and /etc/ssl/sac/bytestash.crt, then run sudo systemctl reload nginx. Do this before others sign in or you connect an AI assistant.
Outbound connections and metadata
ByteStash makes no outbound connection unless you turn on single sign-on. ByteStash and nginx cannot reach the instance metadata service, the Amazon Time Sync Service at 169.254.169.123 keeps working, and the firmware-metadata refresh is masked:
systemctl show -p IPAddressDeny bytestash.service nginx.service chronyc -n sources | grep 169.254
FW=$(systemctl is-enabled fwupd-refresh.timer fwupd-refresh.service || true); echo "$FW" [ "$(sort -u <<<"$FW")" = masked ] && [ "$(systemctl show -p IPAddressDeny --value bytestash.service nginx.service | grep -c 169.254.169.254)" = 2 ]IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 ^* 169.254.169.123 3 4 37 11 +5785ns[ -39us] +/- 522us masked maskedMaintenance
- Your password: change it from the user menu (top right), under Change password. Then update
/root/bytestash-credentials.txtor delete it. - More accounts: sign-up is closed. To let colleagues create their own accounts, set
ALLOW_NEW_ACCOUNTS=truein/etc/bytestash/bytestash.envand runsudo systemctl restart bytestash. Let them register, then set it back tofalseand restart again. Anyone who can reach the instance can register while it istrue, so restrict the security group first. Accounts are administrators only if their name is inADMIN_USERNAMES(defaultadmin). Administrators manage users, snippets, API keys and share links in the Admin panel (user menu). - Single sign-on (OIDC): set
OIDC_ENABLED=trueandOIDC_ISSUER_URL,OIDC_CLIENT_IDandOIDC_CLIENT_SECRETfrom your identity provider in/etc/bytestash/bytestash.env. Registerhttps://<your-dns-name>/api/auth/oidc/callbackas the redirect URL with the provider, and restart ByteStash. Only root can read the file. - Do not set
DISABLE_ACCOUNTS=true. It turns every visitor into one shared anonymous user with full access, and the start guard refuses it. - Backups: ByteStash keeps everything in the SQLite database in
/var/lib/bytestash/snippets. Stop ByteStash before you copy it (sudo systemctl stop bytestash), and start it again afterwards. Keep the backup as safe as the credentials, because it holds your snippets, your API keys and the password hashes. Never write to the database while ByteStash is running. - Upgrades: to move to a newer release, launch the newer version from the listing when it is published and restore your backup.
- Logs: run
sudo journalctl -u bytestash -n 100 --no-pager, and read nginx's access log in/var/log/nginx/access.log. - Security patches: unattended-upgrades stays enabled, so the OS keeps receiving security updates.
Security notes
- Keep the security group tight. Open port 22 only to your management address, and ports 80 and 443 only to the people and API clients that use ByteStash. Public snippets and share links are readable by anyone who can reach port 443.
- Use your own certificate before other people sign in or you connect an AI assistant, so that they trust it without exceptions.
- Keep ByteStash on loopback. Reach it through nginx, which keeps forwarded headers honest and refuses cross-site requests.
- Give each script and assistant its own API key, and delete keys you no longer use.
Support and licensing
ByteStash is developed by Jordan Dalby and contributors and distributed under the GNU General Public License v3.0. This image ships ByteStash 1.6.0 built from the unmodified 1.6.0 source, with two configuration-level edits and the dependency updates listed above. The two edits make ByteStash listen on 127.0.0.1 and trust forwarded headers only from the local nginx.
The complete 1.6.0 source archive, the licence and the build record are in /usr/share/doc/sac-bytestash/. The changed package manifests and lockfile are in /opt/bytestash. Node.js is distributed under the MIT License. SecureAnyCloud provides the packaging, the hardening, first-boot HTTPS and per-instance credentials, and 24/7 support.
SecureAnyCloud is not affiliated with or endorsed by the ByteStash project or its authors. Product names are marks of their owners and are used here only to identify the software.
For help with deployment or configuration, email sales@secureanycloud.com or visit SecureAnyCloud support.
Deploy on AWS
Launch ByteStash 1.6.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com