Dinesh/ Blog
python

Dev Environment — Linux/WSL, Terminal, VS Code

Set up and diagnose a development environment with explicit WSL, virtual-environment, editor, package and platform boundaries.

22 min readChecking device speech…
Lesson preparation & details

Level: beginner

Python and software engineering · Lesson 1 of 15 ↗
Loading this browser’s progress…

By the end, you should be able to

  • Virtual environments isolate Python packages, not the OS or native libraries

Bring with you

Sources checked: . A source check does not change the publication or update date.

Editorial review: · What review means

In this article · 21 sections

Review boundary and corrected contracts

Virtual environments isolate Python packages, not the OS or native libraries. Activation is optional: invoke the environment interpreter directly. Removed install-pipe execution, uncontrolled filesystem overwrite and unsupported WSL speed/support-ticket claims; environment reproduction also needs runtime, platform and artifact provenance.

The examples below teach the stated input contracts; fragments and deliberately incorrect counterexamples are exercises, not production implementations. Complexity counts elementary operations unless a bit-cost or allocation caveat is stated. Practice times are suggestions, not measured performance or hiring guarantees.

Learning objective: Specify and rebuild a development environment on a compatible machine, checking interpreter, package and filesystem boundaries. Fifteen minutes is a practice target, not a cross-platform guarantee.

Why this session exists

Environment failures often come from differences in interpreter versions, packages, operating-system libraries, permissions or configuration. A scripted setup makes those differences inspectable; it does not eliminate them. This session turns an existing shell into a small reproducible project, then explains the optional Windows/WSL and uv setup paths.

By the end you will:

You will be able to

  • Explain WSL, virtual environments, and dotfiles to a non-technical friend in under a minute each.

  • Rebuild the project from reviewed commands and a recorded platform/runtime contract; measure setup time rather than promising it.

  • Diagnose three common environment failures: CRLF scripts, cross-filesystem I/O and the wrong Python interpreter.

  • Connect VS Code to WSL when on Windows; understand reviewed uv installation, run a Python smoke test, and record dependencies without mistaking a freeze for a lockfile.

Prerequisites

No prior programming is required. The local exercise needs Bash and an installed Python 3.12+ with venv support; the optional WSL path needs a supported Windows host and installation permissions. Do not change a managed machine without its owner’s policy.


Pre-read · skim before the session (25 min total)

Further reading: Install WSL

Further reading: Missing Semester · Course Overview + The Shell

Further reading: uv — an extremely fast Python package manager


(a) Intuition · 5 min

What a dev environment actually is

Real: You've just moved into a new professional kitchen. Before you cook anything, you sharpen the knives, arrange the spices so your hand can find cumin without looking, and check that the stove actually turns on. That's 90 boring minutes. Then for the next six months, you cook 300 meals without a single "where did I put the salt?" moment. The 90 minutes paid for itself before dinner two.

 
    A dev environment is the same. Record the shell, editor, package manager and runtime deliberately. Keep setup instructions in Git and revisit them when versions or deployment requirements change.
    Skip this step and you'll spend the next six months fighting Windows line endings, wrong Python versions, and "why is my hot-reload 8 seconds?" mysteries instead of learning ML.
  

The three problems it solves

Every environment tool exists to solve one of these

  • Reproducibility — a fresh machine (or a coworker) can rebuild what you have from a script, not from memory.

  • Isolation — this project's Python 3.12 + pandas 2.2 doesn't collide with next project's Python 3.9 + pandas 1.5.

  • Portability — matching production’s runtime and OS reduces differences. Servers also run other operating systems; matching Linux alone does not reproduce CPU, drivers, network, data or permissions.

WSL (Windows Subsystem for Linux) provides a way to run Linux tooling alongside Windows without manually maintaining a separate dual-boot workflow. WSL 2 ships a real Linux kernel that runs quietly next to Windows, shares clipboard and files, and lets VS Code on Windows edit files inside Linux transparently. macOS includes a Unix shell, but its kernel and several command-line utilities differ from Linux. Use the macOS branches of installation instructions rather than assuming Ubuntu commands work there.

Place files near the tools that use them

For Linux tooling under WSL, a project in the Linux filesystem often avoids cross-filesystem overhead. The right location depends on which tools access it; no universal 10–100× speed or internal support-ticket ranking is established here.

A quick history so you know why the world looks like this

The useful historical change here is architectural: WSL 1 translates Linux system calls, whereas WSL 2 runs an actual Linux kernel in a managed VM. Editors and package tools sit above that boundary; neither VS Code nor uv turns macOS into Linux. Microsoft’s WSL comparison recommends WSL 2 generally but documents cases where WSL 1 remains useful, including workflows spanning Windows and Linux filesystems. This replaces a misleading release-date/speed timeline with the compatibility decision it was meant to teach.

(b) From the Windows host to the Python process · 15 min

The stack on Windows (delete WSL for macOS/Linux)

These tools occupy different layers of one environment. Follow the connection from the Windows editor into Linux, then locate the filesystem where the tools will read and write your project.

The stack on Windows (delete WSL for macOS/Linux)
Figure text equivalent — labels and relationships
  • Node “VS Code (GUI editor)” (square).
  • Node “bash / zsh shell” (square).
  • Node “ext4 filesystem — ~/projects” (square).
  • Node “git · python · node · uv · docker” (square).
  • Group “WSL 2 · real Linux kernel” contains “bash / zsh shell”, “ext4 filesystem — ~/projects”, “git · python · node · uv · docker”.
  • Group “Windows 11 host” contains “VS Code (GUI editor)”, “WSL 2 · real Linux kernel”.
  • “VS Code (GUI editor)” leads to “bash / zsh shell”: Remote-WSL extension. Line: normal; ends: arrow point.
  • “bash / zsh shell” leads to “git · python · node · uv · docker”. Line: normal; ends: arrow point.
  • “git · python · node · uv · docker” leads to “ext4 filesystem — ~/projects”. Line: normal; ends: arrow point.

The important boundary is not between the editor and its terminal window; it is between the host and the filesystem used by the running tools. Keeping the project inside Linux lets the install steps below build on that arrangement.

The install path — initial setup and later maintenance

wsl --install

Desc: On a supported Windows version, review wsl --install in an administrator PowerShell. It enables required components and installs Ubuntu by default; restart if instructed. Confirm the distribution and version with wsl -l -v. This is a machine-changing installation, not part of the local fixture.

Set username + password

Desc: This is your Linux identity. Independent of your Windows login.

apt update && upgrade

Desc: On Ubuntu, sudo apt update refreshes package indexes; sudo apt upgrade upgrades installed packages. Review the proposed upgrades rather than adding automatic confirmation blindly. These are separate operations; neither is a macOS command.

Install VS Code (Win side)

Desc: Then add the ‘WSL’ extension from the marketplace.

code . inside a project

Desc: Status bar should show ‘WSL: Ubuntu’ — you're connected.

Directory layout you'll use for six months

~/ ← your Linux home (aka /home/<user>)└── projects/ └── learning/ ← the 6-month plan lives here ├── s001/ │ ├── .venv/ ← isolated Python — never committed │ ├── hello.py │ └── requirements.txt ├── s002/ └── s003/
Linux home → projects → learning → s001/s002/s003. s001 contains isolated/uncommitted .venv, hello.py, requirements.txt. Home/learning annotations are retained.
Figure text and layout
~/                              ← your Linux home (aka /home/<user>)
└── projects/
    └── learning/               ← the 6-month plan lives here
        ├── s001/
        │   ├── .venv/          ← isolated Python — never committed
        │   ├── hello.py
        │   └── requirements.txt
        ├── s002/
        └── s003/
Text diagram

Rules to internalise:

Name: ✅ Do this

Tagline: Code in Linux filesystem

  • ~/projects/… (native ext4)

  • One folder per session

  • A venv per folder

  • requirements.txt in Git

  • dotfiles in a separate repo

Name: ❌ Not this

Tagline: Code on Windows drive

  • /mnt/c/Users/… for Linux-heavy file workloads without checking cross-filesystem costs

  • One big Documents/Code folder

  • Global pip install for everything

  • ‘I'll remember what I installed’

  • config only in your head

The mental model to hold

Two pieces of glass, one kitchen

VS Code is a windowpane on Windows. Your code, tools, terminal, and Python all live behind that pane in Linux. When permissions, CRLF line endings or file watchers behave differently, inspect both the executing process and the filesystem. Crossing this boundary is one possible cause, not a measured 90% diagnosis.

Myth: "My dev environment is set up — Python is installed, VS Code works, I ran pip install and it worked. Environment setup is a one-time chore I can now forget about."

Truth: An environment is not a state you reach, it's a specification you keep. The only environment that matters is the one you can reproduce on a machine you have never touched, from a file in version control, without you present.

Why: Because on day one it genuinely is one-time. You install once, it works, nothing breaks — for weeks. The cost is deferred, not absent. It arrives the day a teammate clones your repo and gets ModuleNotFoundError, or CI fails on a version you upgraded locally six months ago and forgot, or a notebook that trains fine on your laptop dies in the cluster. Every one of those is the same bug: the environment lived in your machine's history instead of in a file.

Test: Inspect the interpreter that actually runs the application, not merely the command named pip:

command -v python3
python3 -c 'import sys; print(sys.executable); print(sys.version); print(sys.prefix != sys.base_prefix)'

The Boolean is normally true inside a venv and false in the base interpreter. In a real project use .venv/bin/python -m pip list to ask that interpreter’s installer about packages; pip list --not-required means “not required by another installed package”, not necessarily your intentionally declared direct dependencies. A fresh rebuild must match the recorded Python/platform and approved artifacts; an installation failure can also be caused by network, permissions or unavailable wheels.

Question: Why do virtual environments exist at all? Language ecosystems could have solved this with a smarter global installer — why is per-project isolation useful for incompatible package requirements?

Premise: A program imports a library by name, not by version. import requests says nothing about which requests.

Because: source code is written once and must keep working as the world moves

Premise: For an ordinary filesystem package, Python’s import machinery searches configured paths such as sys.path; caches, built-in modules and import hooks also participate.

Because: name resolution needs a deterministic winner, and "first on the path" is the cheapest rule

Premise: If there is exactly one shared site-packages directory, then there is exactly one version of each library visible to every program on the machine.

Because: one directory can only hold one requests/ folder

Premise: But two projects on the same machine legitimately need different versions — project A pinned to an old API, project B needing a new feature. This is not a mistake; it is a common dependency-management situation.

Because: libraries make breaking changes on their own schedule, not yours

Premise: If A strictly requires requests version X and B strictly requires an incompatible version Y under the same import name, one shared package location cannot satisfy both at once. Separate environments give the two processes different package search paths.

Because: the constraint set is genuinely unsatisfiable, so no algorithm helps

A virtual environment configures the interpreter prefix and its package paths. Activation adjusts the current shell PATH; invoking .venv/bin/python directly works without activation. It is dependency isolation, not a security sandbox.

Name: The machine is a cache, the repo is the truth

Picture: Treat your laptop like a CPU cache in front of your repository. Anything valuable that exists only in the cache — an installed package, an exported variable, a hand-edited config, a one-off pip install — is not saved. It is a cache line that will be evicted, by a new laptop, a new teammate, or a CI runner.The question for every setup action is not "does it work now?" but "which file recorded this?" If the answer is "none", the setup action may work locally but is not yet part of a reproducible project specification.

  • Every install must be written down: requirements.txt / pyproject.toml / environment.yml. An undeclared dependency is a future outage with a delay fuse.

  • Pin versions for anything that ships or trains. Floating versions mean your build depends on the date it ran.

  • Secrets are the one thing that must not be in the repo — so they get an explicit mechanism (env vars, key vault), plus a committed .env.example naming them.

  • The definition of done for setup is a clean-machine rebuild, not a working laptop.

Trigger: Fire this model the moment you see: "works on my machine" · a pip install typed into a terminal and not into a file · a notebook that needs a magic cell to run · onboarding docs written in prose instead of a script · a CI failure that reproduces nowhere locally.

Decision: How much isolation do you buy for a project — plain virtualenv, conda, or a container?

Name: venv + pip

Gain: lightweight Python package separation built on an existing interpreter; available with Python, though some Linux distributions package venv support separately. Activation commands and binary layouts differ across operating systems.

Cost: isolates Python packages only — not the interpreter version, not system libraries like BLAS/CUDA/glibc; native wheels can still fail on a different OS

Pick: pure-Python work, libraries, CLI tools, and anything where the team already shares an OS and interpreter version

Name: conda / mamba

Gain: manages the interpreter and non-Python binaries too (MKL, CUDA toolkit, geospatial C libs), which is exactly where scientific stacks break

Cost: another package resolver and binary distribution/channel policy to maintain. Mixing pip and conda modifications needs care; solving time depends on the environment and constraints.

Pick: numerical or ML work with compiled dependencies you do not want to build yourself — the classic case being a GPU stack

Name: Container (Docker / devcontainer)

Gain: packages application userspace into an image that can be shared between development and deployment. It still depends on a compatible kernel, architecture, drivers, configuration and mounted data; using a devcontainer does not prove production parity.

Cost: build times, image size, a filesystem/networking boundary in your inner loop, GPU passthrough friction, and a second skill the team must maintain

Pick: the code will run in production on someone else's infrastructure, or the environment involves system packages that would otherwise be documented in prose

Verdict: Senior engineers pick the lightest isolation that makes the rebuild reproducible, and escalate only when a real failure proves it insufficient. Start with venv; move to conda when a compiled dependency bites; move to containers when the deployment target stops matching your laptop.The layers compose — a container that internally installs from a lockfile is common and correct. What is not correct is skipping the lockfile because you have a container: the image is then reproducible only until its base tag moves.


(c) Hands-on · 25 min

Read the script before running it in Bash on Linux, WSL or macOS with an already installed Python 3.12+. It creates a new temporary project and venv; it does not install Python or uv. Run with bash setup.sh (no executable bit needed), or add a Bash shebang and execute permission for ./setup.sh. If your distribution lacks venv/ensurepip, stop and use its documented installation process.

# Requires an already installed Python 3.12+ interpreter.
# New disposable workspace; no downloads or existing-file overwrite.
WORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/python-workshop.XXXXXX")
printf 'Workspace: %s
' "$WORK_DIR"
cd "$WORK_DIR"
python3 -m venv .venv
.venv/bin/python -c 'import sys; print(sys.executable); print(sys.version)'
.venv/bin/python -m pip --version
# Install project dependencies only after reviewing their pinned requirements.
# Use .venv/bin/python directly in automation; activation is optional.

What each block does

mktemp -d chooses a new directory, python3 -m venv creates its environment using that existing interpreter, and the explicit .venv/bin/python avoids activation state. The two inspection commands report the chosen interpreter and pip association. Inside that new project, create hello.py containing print("hello from the project") and run .venv/bin/python hello.py; the expected line is hello from the project. Do not overwrite an existing project to perform this exercise.

A richer provenance smoke test. The original hello.py also reports runtime/platform context. Keep this version when you need to compare environments; machine-specific values are observations, not proof of Windows/WSL behavior or of a matching production host. Do not publish a hostname/path if your organization treats it as sensitive.

"""hello.py — verify the environment is real."""
import sys, platform, os, socket
 
print("python  :", sys.version.split()[0])
print("os      :", platform.system(), platform.release())
print("host    :", socket.gethostname())
print("cwd     :", os.getcwd())
print("shell   :", os.environ.get("SHELL", "unknown"))
print("hello, dev environment ✅")

Run it with the selected environment interpreter, then compare its output with the recorded build contract. The simple hello line checks invocation; this richer version explains why the same source might behave differently elsewhere.

Optional uv route (not executed by the fixture). First install uv by an approved method from its official installation page; inspect rather than pipe a downloaded script into a shell. With uv already installed, uv python install 3.12 obtains a managed interpreter, uv venv --python 3.12 .venv creates an environment, and uv pip install --python .venv/bin/python -r requirements.txt installs reviewed requirements. The first and third commands can download and install software. Record uv --version, the exact interpreter build and platform; a 3.12 selector alone does not pin its patch release.

After a deliberate installation, uv pip freeze --python .venv/bin/python prints installed versions in requirements format. Review it before saving a new requirements.txt; redirecting with > truncates an existing file. A freeze is an inventory, not a solver-produced lock or a promise of bit-for-bit reproducibility. Use a project lock workflow (for example uv lock and uv sync --locked) when appropriate, retain artifact hashes/index provenance and test on the target platform. Never commit credentials embedded in package URLs.

The revised workshop starts in a new temporary directory, uses project-local settings, and prints enough state to inspect what happened. Existing projects, global shell configuration and remote repositories are not modified. The old automatic installer and existing-file-overwrite walkthrough is intentionally not executable guidance; use the documented version-specific tools only after reviewing the proposed action.

Isolation check before changing configuration. The original requests experiment is useful only with explicit prerequisites. In a newly created venv where requests has not been installed, this program should fail to import it:

import requests
print("requests version:", requests.__version__)

Do not assume the system interpreter has requests, and do not treat one import failure alone as proof of all isolation boundaries. First inspect the chosen executable and sys.prefix; after a separately authorized install into that same environment, the program should report its installed version. No requests package is installed in this review. The no-download fixture instead places a tiny locally authored module in one disposable venv and confirms that a second isolated venv cannot import it. Both interpreters still access the host filesystem: package separation is not a security sandbox.

Bonus — a first dotfile

A dotfile is a configuration file conventionally named with a leading dot. Keep a reviewed template in a separate repository, excluding secrets. Inspect your existing shell startup file and merge selected lines; never replace it wholesale. These Bash examples are optional:

# ~/.bashrc additions — first version, will grow
alias ll='ls -alF'
alias ..='cd ..'
export PATH="$HOME/.local/bin:$PATH"    # useful only if the chosen installer puts tools here

Then source ~/.bashrc (or open a new terminal). ll now lists files with details; .. jumps up a directory. S004 teaches reading version-matched documentation, which helps you maintain this configuration. A new shell must load the right startup file; Bash and zsh do not share all syntax.


(d) Production reality · 15 min

The following are diagnostic cases, not verified company incidents or predictions about your first month.

  • CRLF script: a shebang ending in CRLF can name an interpreter with a stray carriage return. Inspect a suspect file with file script.sh or read its bytes; b"\r\n" versus b"\n" distinguishes the encodings. In a reviewed .gitattributes, *.sh text eol=lf declares LF for shell scripts. This does not retroactively fix every existing working copy; review normalization diffs before committing.
  • Cross-filesystem I/O: if Linux tools repeatedly inspect a project under /mnt/c, compare the same operation on a copy in the Linux filesystem. Record tool version, file count, warm/cold caches and timing. This local Linux fixture cannot measure WSL performance.
  • Wrong interpreter: a successful install followed by ModuleNotFoundError can mean the install and run used different Python executables. Compare sys.executable and .venv/bin/python -m pip --version; choose the same environment in VS Code. Other causes include a misspelled import or a missing dependency.

How top teams handle this

A VS Code devcontainer uses a .devcontainer/devcontainer.json specification to select an image/Dockerfile or Compose setup and configure tools, extensions and lifecycle commands. Review those commands before opening an unfamiliar repository in a container. It need not be Ubuntu; downloads, image builds and machine capacity determine startup time.

The rebuild rule

Keep the project specification and smoke test in version control, separate secrets from that specification, and rehearse a clean rebuild on a compatible machine. Record what was and was not reproduced. A devcontainer reduces userspace drift; it cannot make drift or support tickets impossible.

Where this shows up in the rest of the plan

Root: Your dev environment feeds every session that follows

Label: S002 · Git & GitHub

Desc: Uses the same shell and editor; version-controlled attributes can document line-ending policy.

Label: S003 · Command line

Desc: Deepens the shell you just installed — pipes, grep, jq.

Label: S004 · Reading documentation

Desc: Teaches how to verify configuration and tool behavior against matching documentation.

Label: S005 · Python — data types

Desc: Runs inside your chosen project venv; uv is an optional creation route.

Label: S056 · Docker

Desc: Containers add a userspace image and runtime boundary; a devcontainer also describes editor and setup behavior.

Label: S060 · Linux fundamentals

Desc: Explains the ext4 filesystem, syscalls, and processes your terminal already uses.


(e) Recall + stretch · 10 min

Self-check (answer follows each question).

Recall — questions and worked answers

Q: In one sentence, what is the difference between WSL 1 and WSL 2?

A: WSL 1 translates Linux system calls; WSL 2 uses an actual Linux kernel in a managed VM. WSL 2 is the general default, but compatibility, memory and cross-filesystem workflows can justify WSL 1. Neither is identical to every production Linux host.

Q: Why should your code live in ~/projects and not /mnt/c/…?

A: For Linux-heavy WSL work, ~/projects normally keeps files on the Linux filesystem and avoids repeated access across the Windows/Linux boundary. If Windows tools own the workload, a Windows location may be appropriate. Measure your workload; there is no universal 10–100× factor.

Q: What problem does a virtual environment solve that installing packages globally does not?

A: Separate package search environments let project A use pandas 2.2 and project B use pandas 1.5 under their compatible Python versions. On Unix, a Python 3.12 venv commonly uses .venv/lib/python3.12/site-packages; Windows layout differs. This does not isolate the operating system or select an arbitrary new interpreter version.

Q: What does chmod +x script.sh do, and why do fresh scripts need it?

A: chmod +x script.sh adds execute permission according to the applicable mode/umask rules; chmod u+x script.sh explicitly grants it to the owner. Direct execution also needs a suitable shebang and an executable filesystem mount. bash script.sh reads the file as Bash input without requiring its executable bit.

Q: You cloned a coworker's project with a requirements.txt and no venv. What is the exact 3-command sequence to reproduce their environment?

A: After selecting a compatible, already installed Python and reviewing the requirements, run python3 -m venv .venv, .venv/bin/python -m pip install -r requirements.txt, then .venv/bin/python -m pip check. The middle command installs software and may access the network; no activation is required. Python version, native dependencies and artifacts still need recording.

Q: Why does uv pip freeze > requirements.txt matter more than pip install did?

A: A freeze records installed package versions; the install alone leaves no reviewed project manifest. It is not a lockfile or a bit-for-bit rebuild guarantee: platform-specific wheels, hashes, indexes and interpreter/native libraries also matter. Review and save the output without accidentally truncating a valued file.

Q: Name two ways to make ‘forgot to activate venv’ impossible.

A: Call .venv/bin/python script.py directly, or use uv run script.py in an appropriately configured uv project. uv run can create/synchronize the environment, so it is not a no-install verification command by default. direnv is another option, but only authorize a reviewed .envrc: it executes code.

Q: What is CRLF, and what one config line prevents it from breaking your bash scripts?

A: CRLF is carriage return plus line feed (\r\n); LF is \n. A CR in the shebang may become part of the interpreter name. Prefer an explicit repository policy such as *.sh text eol=lf, review normalization changes, and check actual bytes. core.autocrlf=input affects conversion on add, not every existing file on disk.

Q: What is a devcontainer, and why do senior teams use them?

A: It is an environment specification, commonly .devcontainer/devcontainer.json, used with an image/Dockerfile or Compose configuration. It can align dependencies and editor tooling, but images, lifecycle scripts, mounted files, drivers and secrets still need review. There is no guaranteed three-minute startup or drift-free production match.

Q: Stretch — Git-init your s001 folder, add a .gitignore that excludes .venv and __pycache__, then commit hello.py and requirements.txt. Why must .venv never be committed?

Stretch: true

A: In a newly created exercise repo, ignore .venv/ and __pycache__/, stage only the reviewed hello.py, dependency specification and .gitignore, inspect git diff --cached, then commit. Venvs contain platform/path-dependent executables and scripts and are intended to be recreated, not copied as a portable source artifact. Size varies; record the interpreter and dependencies rather than committing the environment.

Explain-out-loud test

If you can't teach these three to a friend without notes, redo the session:

  1. What is a dev environment? (one sentence, no jargon)
  2. What is a virtual environment, and why? (one concrete example)
  3. What is one thing you should never do in a dev environment? (and why)

Model answers: (1) It is the tools and configuration used to build and run a project. (2) A venv lets two projects select different package versions without sharing one package directory. (3) Never commit real credentials; use a protected external secret mechanism and commit only nonsecret configuration names.


What comes next

Hub: The 6-Month Learning Plan


Part of a 130-session learning series. Suggested session: 70 minutes across the five labeled blocks, plus 20 minutes for setup, questions and retesting. Optional pre-reading is separate; these are pacing suggestions, not completion guarantees.

Corrective source notes

Additional governing references: WSL comparison, uv Python installation, uv environments, pip freeze, and Git attributes.

These citations support the named claims, not blanket certification of the lesson. Worked proofs are derived in the text; execution scope and finite-test limits are recorded separately.

Pause / Recall / Apply

Can you explain it without the page?

Close the example. Reconstruct the core idea, then change one assumption. Mark complete when you’re ready; you can always undo it.

Stored in this browser only. No account, no sync. Clearing browser data removes your record.