Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Instructor Setup: API Keys in a Shared .env File

!! This notebook is meant to be run by the instructor, not students !!

This notebook walks an instructor through placing API keys (OpenAI, Anthropic, etc.) into a .env file stored in a shared-readwrite folder so that all student notebooks can load them without ever seeing the raw key values.

Why a shared .env file?

  • Security: The key is never hard-coded in a notebook and never committed to GitHub.

  • Convenience: One update to the shared .env file rotates the key for every dependent notebook.

  • Transparency: Students learn the best-practice pattern of loading secrets from environment variables.

What this notebook does

  1. Confirms the path to the shared-readwrite folder.

  2. Creates the folder if it does not yet exist.

  3. Writes (or updates) a .env file in that folder with the API keys you supply.

  4. Verifies that the keys can be loaded back with python-dotenv.

Step 1 — Locate (or create) the shared-readwrite folder

Pick the approach that matches your environment and uncomment the relevant block.

Approach 1 — JupyterHub with a shared filesystem (e.g., Berkeley DataHub / Cloudbank Demo / Cal-ICOR)

We are using the bash ls command just to check what folders are available

# Cal-ICOR / generic JupyterHub shared folder
# Uncomment the line that matches your hub's layout:

!ls /home/jovyan/shared-readwrite                        # Cloudbank Classroom workshop hub
# !ls /home/jovyan/_shared/econ148-readwrite     # Berkeley DataHub SP25

Approach 2 — Local machine or a relative path inside this repo

# Relative path (sibling folder next to the repo root notebooks)
# !ls ../shared

# Absolute path on a laptop:
# !ls /Users/yourname/Documents/GitHub/SmallLM-FA25/shared-rw

Set shared_folder_path to the correct path for your environment to “Write” to

JupyterHub shared folder in the current setup has "shared’ for read-only and “shared-readwrite” for read-write, so we point to the read-write one. For local use, you can create a “shared-rw” folder in the repo or point to any other directory where you want to store the .env file.

import os

# ✏️  Edit this to match your setup:
shared_folder_path = "/home/jovyan/shared-readwrite"

# Expand to an absolute path so every subsequent cell works regardless of cwd
shared_folder_path = os.path.abspath(shared_folder_path)
print(f"Shared ReadWrite folder path: {shared_folder_path}")

Step 2 — Create the shared folder if it does not exist

Step 3 — Enter your API keys

Fill in the API key variables below.
Leave a key as an empty string "" if you do not have it — it will be skipped.

⚠️ Never commit this notebook after filling in real keys.
The keys are written to the shared .env file and this notebook should be cleared before saving.

# ✏️  Paste your API keys here (leave blank to skip):
openai_api_key     = ""   # e.g. sk-...
anthropic_api_key  = ""   # e.g. sk-ant-...
nrp_api_key        = ""   # e.g. sk-nrp-

Step 4 — Write the .env file to the shared folder

If a .env file already exists, this cell merges the new values in: existing keys not listed above are preserved, and the keys you supplied above are added or updated.

env_file_path = os.path.join(shared_folder_path, ".env")

if os.path.isfile(env_file_path):
    print(f"Found existing .env at {env_file_path}")
else:
    with open(env_file_path, "w") as f:
        f.write("")
    print(f"No .env found — created {env_file_path}")
env_file_path = os.path.join(shared_folder_path, ".env")

# --- Ensure file exists ---
if os.path.isfile(env_file_path):
    with open(env_file_path, "r") as f:
        existing_lines = f.readlines()
else:
    existing_lines = []
    print(f"No .env found — creating {env_file_path}")

# --- Build dict of keys to write ---
new_keys = {}

if openai_api_key and openai_api_key.strip():
    new_keys["OPENAI_API_KEY"] = openai_api_key.strip()

if anthropic_api_key and anthropic_api_key.strip():
    new_keys["ANTHROPIC_API_KEY"] = anthropic_api_key.strip()

if nrp_api_key and nrp_api_key.strip():
    new_keys["NRP_API_KEY"] = nrp_api_key.strip()

# --- Merge existing keys with new ones ---
updated_keys = set()
merged_lines = []

for line in existing_lines:
    stripped = line.strip()

    # keep comments or malformed lines unchanged
    if stripped.startswith("#") or "=" not in stripped:
        merged_lines.append(line)
        continue

    key = stripped.split("=", 1)[0].strip()

    if key in new_keys:
        merged_lines.append(f'{key}="{new_keys[key]}"\n')
        updated_keys.add(key)
    else:
        merged_lines.append(line)

# --- Append keys not already present ---
for key, value in new_keys.items():
    if key not in updated_keys:
        merged_lines.append(f'{key}="{value}"\n')

# --- Write file ---
with open(env_file_path, "w") as f:
    f.writelines(merged_lines)

print(f"\n✅ .env written to: {env_file_path}")

if new_keys:
    print("Keys written:", ", ".join(new_keys.keys()))
else:
    print("No keys supplied — file unchanged")

Step 5 — Verify the keys load correctly

# check the filepath 
env_file_path = "/home/jovyan/shared/.env"
try:
    from dotenv import load_dotenv
except ImportError:
    %pip install python-dotenv
    from dotenv import load_dotenv
from dotenv import load_dotenv
import os

load_dotenv(env_file_path, override=True)

loaded_openai    = os.getenv("OPENAI_API_KEY")
loaded_anthropic = os.getenv("ANTHROPIC_API_KEY")
loaded_nrp      = os.getenv("NRP_API_KEY")

print("OpenAI API key loaded:    ", "✅" if loaded_openai    else "❌ not found")
print("Anthropic API key loaded: ", "✅" if loaded_anthropic else "❌ not found")
print("NRP API key loaded:      ", "✅" if loaded_nrp      else "❌ not found")

Notes for Instructors

  • The .env file at <shared_folder>/.env is intentionally not tracked in this repository.
    Make sure it is listed in .gitignore or kept outside the repo entirely.

  • Student notebooks load the key with:

    from dotenv import load_dotenv
    import os
    load_dotenv('../shared/.env')
    key = os.getenv('openai_API_KEY')
  • Students should never print the raw key value or share the .env file.

  • To rotate a key, just re-run this notebook with the new key — all student notebooks will pick it up automatically.

  • On JupyterHub, ensure the shared folder has read permissions for all students and write permission only for instructors.

NotebookDescription
HuggingFace_Hub_Download_gguf.ipynbDownload GGUF models into a shared folder
OpenAI_API.ipynbUse the OpenAI API (loads key from .env)
Anthropic_API.ipynbUse the Anthropic API (loads key from .env)
#!cat /home/jovyan/shared/.env