Skip to content

Latest commit

Β 

History

1,972 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

dotfiles

Highly opinionated development environment for AI safety research. ZSH, Tmux, Vim, SSH, and AI coding assistants across macOS, Ubuntu, and cloud containers.

This setup reflects workflows optimized for ML research: reproducibility, experiment tracking, async API patterns, and rigorous methodology. The AI assistant configurations enforce research disciplineβ€”interview before planning, plan before implementing, skepticism of surprisingly good results.

Key highlights:

  • πŸ€– AI Coding Assistants - Extensively configured Claude Code and some Codex support
  • πŸ‘» Sensible defaults for Ghostty
  • πŸ¦€ Rust-powered CLI tools - Modern, blazing-fast replacements for standard Unix utilities
  • 🧹 Automatic cleanup - Scheduled cleanup of Downloads/Screenshots (macOS, moves to trash)

Originally forked from jplhughes/dotfiles - thanks John for the solid foundation!

AI agents working here: start with CLAUDE.md β€” top rules, a common-tasks table, and pointers into docs/deploy-components.md (deploy behavior, cloud, extending) and docs/tooling-and-packages.md (packages, symlink-vs-copy, gotchas). This README is human-oriented onboarding; CLAUDE.md is the operational doc.

Quickstart

This project offers two quickstart paths: Local and Cloud.


Local Quickstart

For setting up on your personal machine (macOS, Linux, desktop/laptop):

git clone https://github.com/yulonglin/dotfiles.git && cd dotfiles

# 1. Install dependencies (zsh, tmux, CLI tools, AI assistants)
./install.sh

# 2. Deploy configurations (symlinks, shell config, secrets, automation)
./deploy.sh

# 3. Restart your shell
source ~/.zshrc
  • install.sh installs required software.
  • deploy.sh deploys config files and settings.
  • Both scripts are idempotent and safe to re-run.

All configuration options are stored in config.sh. Flags are additive (e.g., --mouseless adds that feature to defaults). Use --minimal to disable most options.


Cloud Quickstart

For cloud environments (RunPod, Hetzner, Lambda Labs, etc):

  1. SSH into your new remote machine as root.

  2. Run the one-liner:

    # RunPod (fresh pod)
    curl -fsSL https://raw.githubusercontent.com/yulonglin/dotfiles/main/scripts/cloud/setup.sh | bash
    
    # Hetzner / standard VPS (persistent /home)
    curl -fsSL https://raw.githubusercontent.com/yulonglin/dotfiles/main/scripts/cloud/setup.sh | USER_HOME=/home bash

    This creates a non-root user in persistent storage (/workspace/yulong on RunPod), copies SSH keys, installs dependencies, clones dotfiles, and runs install.sh --profile=cloud + deploy.sh --profile=cloud (a lean remote-dev set β€” no pueue/zotero/Rust toolchain). It will prompt for GitHub auth.

    Provisions the main branch by default. To pin another branch, pass --branch (use bash -s -- to forward args through curl | bash) or set DOTFILES_BRANCH:

    curl -fsSL https://raw.githubusercontent.com/yulonglin/dotfiles/main/scripts/cloud/setup.sh | bash -s -- --branch yulong
  3. Reconnect as your user:

    ssh yulong@<ip>
  4. (Optional) After pod restart (RunPod recreates /etc/passwd):

    curl -fsSL https://raw.githubusercontent.com/yulonglin/dotfiles/main/scripts/cloud/restart.sh | bash
  5. (Optional) Customize components: Edit config.sh to disable resource-intensive options (AI assistants, cleanup automation, etc.) before running install/deploy.

Tip: The setup auto-detects cloud providers and adjusts accordingly (persistent storage paths, SSH config, no macOS-only features). See scripts/cloud/README.md for details.

Installation

Step 1: Install dependencies

Install dependencies (e.g. oh-my-zsh and related plugins). The installer auto-detects your OS and applies sensible defaults.

# Install with defaults (recommended)
./install.sh

# Install only specific components
./install.sh --minimal --tmux --zsh  # --minimal disables all defaults

Defaults by platform:

Platform Defaults
macOS zsh, tmux, AI tools, cleanup + Rust CLI tools via Homebrew
Linux zsh, tmux, AI tools, create-user + Rust CLI tools via Homebrew (Linuxbrew)

Installation on macOS requires Homebrew - install from brew.sh first if needed.

The Rust CLI tools installed by default: bat (cat), eza (ls), fd (find), ripgrep (grep), delta (diff), zoxide (cd), dust (du), jless (JSON viewer). --extras adds hyperfine, gitui, and code2prompt.

Step 2: Deploy configurations

Deploy configurations (sources aliases for .zshrc, applies oh-my-zsh settings, etc.). All settings live in config.sh β€” edit once, deploy everywhere.

# Deploy with defaults (recommended)
./deploy.sh

# Profiles
./deploy.sh --profile=server    # Safe base for shared machines
./deploy.sh --profile=minimal   # Nothing enabled β€” specify what you want

# Deploy only specific components
./deploy.sh --only vim claude   # Only vim and claude, nothing else

# Add to defaults
./deploy.sh --mouseless         # Defaults + mouseless

Default components:

  • Shell: ZSH, tmux, vim, Powerlevel10k
  • Editors: VSCode/Cursor/Antigravity (merged settings), Zed (symlinked config + keymap), .editorconfig, .curlrc, .inputrc
  • AI tools: Claude Code, Codex CLI, Serena MCP, Ghostty terminal
  • Git: gitconfig, global gitignore/gitattributes, global git hooks (secret detection)
  • Dev tools: htop, pdb++, matplotlib styles, claude-tools Rust binary
  • Secrets: GitHub gist sync, Bitwarden Secrets Manager (BWS)
  • Supply chain: 7-day quarantine for npm/bun/pnpm/uv, weekly dep-audit
  • Automation: file cleanup (macOS), Claude Code session cleanup, AI tools auto-update, package auto-update, text replacements sync (macOS)

Flags are additive β€” e.g., ./deploy.sh --mouseless deploys defaults + mouseless. Use --minimal to disable all defaults, then specify only what you want.

Adopting These Dotfiles

This repo is highly personal β€” it reflects one person's workflow, opinions, and tooling choices. The best way to use it is to point a coding agent at this repo and ask it to extract the parts you find useful into your own dotfiles.

Generalizable (worth extracting):

  • Shell config (zsh/tmux/p10k)
  • Modern CLI tools (bat, eza, fd, rg, etc.)
  • Git config + global gitignore/gitattributes
  • Editor settings (VSCode/Cursor merge logic)
  • Cleanup automation (Downloads/Screenshots)
  • Gist sync (bidirectional SSH config/identity sync)
  • BWS encrypted secrets workflow

Personal (skip or replace):

  • Claude Code plugins/agents/skills
  • Website alias, SSH host colors
  • Mouseless config
  • Ghostty theme aliases
  • Specific API keys and gist IDs
  • Cloud setup scripts (RunPod user)
  • Plugin marketplace selections

All personal values are centralized in config.sh β€” edit DOTFILES_USERNAME, DOTFILES_REPO, GIST_SYNC_ID, GIT_USER_NAME, and GIT_USER_EMAIL to make it yours.

Secrets & Security

Encrypted Secrets (Bitwarden Secrets Manager)

API keys are stored in Bitwarden Secrets Manager (BWS) β€” a hosted, team-shareable secrets vault. The CLI (bws) fetches secrets on demand; nothing is written to disk except a machine access token (at ~/.config/bws/token).

One command, secrets. Typing it bare prints a status header β€” backend, token path, this repo's bindings, ambiguous env names, permission problems β€” and then offers a menu, but only at an interactive terminal with gum installed. Piped or scripted (secrets | cat), it prints the header and exits 0, so a hook or CI job can call it unconditionally.

secrets                  # Status header, plus a menu at an interactive terminal
secrets init             # First-time setup on this machine: save the BWS access token
secrets edit             # Add/update/delete secrets (fzf TUI, or: secrets edit KEY VALUE)
secrets ls [ENV]         # Env names, their active BWS key, blocked siblings
secrets get ENV          # Print one value to stdout
secrets use ENV          # Choose which BWS key an ambiguous env name resolves to
secrets envrc [KEY...]   # Wire this repo's .envrc (direnv, resolved live)
secrets run KEY... -- CMD   # Run CMD with only those keys in its environment
secrets doctor           # Show backend paths and repair file permissions

secrets get prints a secret; secrets run uses one without printing it. Prefer run in anything whose output is logged or pasted.

New machine setup:

  1. Run ./install.sh (installs bws CLI)
  2. Run secrets init and paste your BWS access token from Bitwarden

Per-project usage: Run secrets envrc in any repo to create a .envrc that selectively exposes only the secrets that repo should see. It supports direct exports (KEY), renamed exports (ENV_VAR=SECRET_NAME), and a repo-specific Telegram plugin binding (--telegram-secret SECRET_NAME). If local .env files already exist, the TUI scans the repo root recursively and can offer to delete selected files.

secrets envrc tries direnv allow automatically. If that cannot update direnv's allowlist (for example in a sandboxed environment), it prints the manual direnv allow . command and still completes the rest of the setup.

Daemon repos that never reach direnv β€” a container, a systemd unit β€” use secrets envrc --plaintext, which materializes real values into .env at mode 600. It refuses to write unless .gitignore already covers .env, and the values are static: rotating the secret in BWS means re-running the command. Everything with a shell session uses plain secrets envrc instead, which resolves live and leaves no secret at rest.

Supply Chain Defense

Multi-layer defense against npm/PyPI supply chain attacks (axios 2026, litellm 2026, shai-hulud 2025). Deployed automatically with ./deploy.sh.

What it does:

Layer Defense What it blocks
7-day quarantine min-release-age on all package managers Freshly-published malicious versions (caught within days)
Script blocking ignore-scripts=true in npm/pnpm Postinstall scripts that exfiltrate secrets or install RATs
Credential isolation No ambient exports; each repo's .envrc binds only its own keys A postinstall script in project A finds none of project B's keys in its environment
Lockfile scanning Pre-commit hook checks changed lockfiles Known-bad packages entering your lockfile
Weekly audit Scans all repos for known-bad IOCs Packages you already have that were later found compromised
Claude Code hook Warns before any npm install / pip install AI assistant installing packages without checking them first

Day-to-day workflow:

# Installing packages works normally β€” quarantine is transparent
npm install express          # Works (express is >7 days old)
bun add zod                  # Works
uv add httpx                 # Works

# New packages published <7 days ago are blocked (intentional)
npm install some-brand-new-pkg
# Error: min-release-age β€” package was published 2 days ago

# Override for a specific install (after checking it's safe)
npm install --min-release-age=0 some-brand-new-pkg   # npm
bun add --minimumReleaseAge=0 some-brand-new-pkg      # bun
UV_EXCLUDE_NEWER= uv pip install some-brand-new-pkg   # uv

Credential isolation:

API keys are NOT exported into every shell, so a compromised postinstall script finds nothing in its environment. Each project's .envrc binds only the keys it needs:

# Interactive picker (fzf)
cd ~/code/my-project
secrets envrc                # Select keys with TAB, confirm with ENTER
# β†’ Creates .envrc with eval-based exports, direnv auto-loads on cd

# Non-interactive
secrets envrc ANTHROPIC_API_KEY OPENAI_API_KEY

# Map a namespaced secret into the env var your app expects
secrets envrc ANTHROPIC_API_KEY TELEGRAM_BOT_TOKEN=NUDGE_TELEGRAM_BOT_TOKEN

# Claude Telegram plugin: keep the token canonical in the secrets store,
# and generate .claude/channels/telegram/.env only at launch time
secrets envrc --telegram-secret AMBASSADOR_TELEGRAM_BOT_TOKEN

# Check what's configured
secrets envrc --list         # Show keys in current .envrc
secrets envrc --clean        # Remove .envrc

# One-off command with selected keys (no .envrc needed)
secrets run ANTHROPIC_API_KEY OPENAI_API_KEY -- python my_script.py

Manual audit:

dep-audit                    # Scan all repos for known-bad packages now
# Runs automatically every Sunday at 10 AM

Config files deployed:

File Deployed to Purpose
config/npmrc ~/.npmrc ignore-scripts=true + min-release-age=7
config/bunfig.toml ~/.bunfig.toml minimumReleaseAge=604800 (seconds)
config/pnpmrc ~/Library/Preferences/pnpm/rc minimum-release-age=10080 (minutes)
config/uv.toml ~/.config/uv/uv.toml exclude-newer (via UV_EXCLUDE_NEWER env var)

Selective deploy:

./deploy.sh --only pkg-configs    # Just package manager configs
./deploy.sh --no-pkg-configs      # Everything except package configs
./deploy.sh --only dep-audit      # Just the weekly audit

Global Git Hooks

Pre-commit hooks for secret detection across all repositories (./deploy.sh --git-hooks, part of defaults). Scans staged files for API keys, tokens, and credentials before each commit.

Where The Detail Lives

Everything else this repo does is documented next to the code it configures:

About

Highly-opinionated setup for AI safety research with coding agents

Resources

Stars

13 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages