diff --git a/bases/rsptx/author_server_api/IMPLEMENTATION_INSTRUCTIONS.md b/bases/rsptx/author_server_api/IMPLEMENTATION_INSTRUCTIONS.md
new file mode 100644
index 00000000..a9b7f481
--- /dev/null
+++ b/bases/rsptx/author_server_api/IMPLEMENTATION_INSTRUCTIONS.md
@@ -0,0 +1,124 @@
+# Implementation Instructions for Git Commit Hash Display
+
+This document provides instructions on how to integrate the git commit hash functionality into the Runestone author server to address issue #733.
+
+## Files Added
+
+### 1. git_utils.py
+This module provides utility functions for retrieving the current git commit hash using multiple fallback methods:
+- Environment variable `GIT_COMMIT_HASH` (useful for CI/CD)
+- Git command execution
+- Reading from `.git` directory files
+
+## Integration Required
+
+### 1. Modify main.py
+
+Add the following import after the existing Local App imports:
+
+```python
+from rsptx.author_server_api.git_utils import get_git_commit_info
+```
+
+Add a new API endpoint before the final endpoint:
+
+```python
+@app.get("/author/commit_info")
+async def get_commit_info(request: Request, user=Depends(auth_manager)):
+ """
+ Get git commit hash information for debugging purposes.
+
+ This endpoint returns the current git commit hash that the server was built from,
+ helping with debugging and version tracking as requested in issue #733.
+ """
+ commit_info = get_git_commit_info()
+ return JSONResponse(commit_info)
+```
+
+Modify the home endpoint to include commit info in the template context. Find the `@app.get("/author/")` endpoint and update it:
+
+```python
+@app.get("/author/")
+async def home(request: Request, user=Depends(auth_manager)):
+ print(f"{request.state.user} OR user = {user}")
+ course = await fetch_course(user.course_name)
+ if user:
+ if not await is_author(user.id):
+ return RedirectResponse(url="/notauthorized")
+ if user:
+ name = user.first_name
+ book_list = await fetch_books_by_author(user.username)
+ book_list = [b.Library for b in book_list if b.Library is not None]
+ else:
+ name = "unknown person"
+ book_list = []
+ # redirect them back somewhere....
+
+ # Add git commit info for debugging
+ git_info = get_git_commit_info()
+
+ return templates.TemplateResponse(
+ "author/home.html",
+ context={
+ "request": request,
+ "name": name,
+ "book_list": book_list,
+ "course": course,
+ "git_commit_info": git_info, # Add this line
+ },
+ )
+```
+
+### 2. Template Updates (Future)
+
+To display the commit hash in the Author Tools page, the `author/home.html` template would need to be updated to include:
+
+```html
+{% if git_commit_info.available %}
+
+
Build Version Info
+
+ Built from commit: {{ git_commit_info.hash }}
+ (source: {{ git_commit_info.source }})
+
+
+{% endif %}
+```
+
+### 3. Book Server Integration (Future Enhancement)
+
+For embedding the commit hash in books themselves, similar integration would be needed in:
+- `bases/rsptx/book_server_api/main.py`
+- Book building processes in the worker functions
+- Book templates to display version info
+
+## Environment Variable Support
+
+For production deployments, set the `GIT_COMMIT_HASH` environment variable during the build process:
+
+```bash
+# In your build script or CI/CD pipeline
+export GIT_COMMIT_HASH=$(git rev-parse --short HEAD)
+```
+
+This ensures the commit hash is available even when the `.git` directory is not present in production containers.
+
+## API Usage
+
+Once implemented, the commit info will be available via:
+- GET `/author/commit_info` - Returns JSON with git commit information
+- Author home page will display the commit hash in the UI
+
+## Testing
+
+To test the functionality:
+1. Check the API endpoint: `curl http://localhost:8000/author/commit_info`
+2. Visit the author home page to see the commit hash displayed
+3. Test with environment variable: `GIT_COMMIT_HASH=abc1234 python -m uvicorn main:app`
+
+## Benefits
+
+- **Debugging**: Easily identify which version of the code is running
+- **Version tracking**: Know exactly what commit books were built from
+- **Cache troubleshooting**: Identify when servers haven't been updated
+- **Multiple fallbacks**: Works in development, CI/CD, and production environments
diff --git a/bases/rsptx/author_server_api/git_utils.py b/bases/rsptx/author_server_api/git_utils.py
new file mode 100644
index 00000000..8b1d16bf
--- /dev/null
+++ b/bases/rsptx/author_server_api/git_utils.py
@@ -0,0 +1,121 @@
+# ************************************************
+# Git Utilities for displaying commit information
+# ************************************************
+# This module provides utilities for getting git commit information
+# to help with debugging and version tracking in Runestone books.
+
+import os
+import subprocess
+from typing import Optional
+
+
+def get_git_commit_hash() -> Optional[str]:
+ """
+ Get the current git commit hash.
+
+ This function attempts to get the git commit hash using multiple methods:
+ 1. From environment variable GIT_COMMIT_HASH (useful for CI/CD)
+ 2. From git command if available
+ 3. From .git/refs/heads/ file if .git directory exists
+
+ Returns:
+ str: The git commit hash (short form - first 7 characters) or None if not available
+ """
+ # Method 1: Check environment variable (useful for CI/CD environments)
+ commit_hash = os.environ.get('GIT_COMMIT_HASH')
+ if commit_hash:
+ return commit_hash[:7] # Return short hash
+
+ # Method 2: Try using git command
+ try:
+ result = subprocess.run(
+ ['git', 'rev-parse', '--short', 'HEAD'],
+ capture_output=True,
+ text=True,
+ timeout=10
+ )
+ if result.returncode == 0:
+ return result.stdout.strip()
+ except (subprocess.SubprocessError, FileNotFoundError, subprocess.TimeoutExpired):
+ pass
+
+ # Method 3: Try reading from .git directory directly
+ try:
+ # Get the current branch
+ git_head_file = '.git/HEAD'
+ if os.path.exists(git_head_file):
+ with open(git_head_file, 'r') as f:
+ head_content = f.read().strip()
+
+ # If HEAD contains a reference to a branch
+ if head_content.startswith('ref: '):
+ branch_ref = head_content[5:] # Remove 'ref: ' prefix
+ branch_file = os.path.join('.git', branch_ref)
+ if os.path.exists(branch_file):
+ with open(branch_file, 'r') as f:
+ commit_hash = f.read().strip()
+ return commit_hash[:7] # Return short hash
+ else:
+ # HEAD contains the commit hash directly (detached HEAD)
+ return head_content[:7]
+ except (OSError, IOError):
+ pass
+
+ return None
+
+
+def get_git_commit_info() -> dict:
+ """
+ Get comprehensive git commit information.
+
+ Returns:
+ dict: Dictionary containing git information including:
+ - hash: commit hash (short)
+ - available: whether git info is available
+ - source: source of the information (env, git_cmd, git_file)
+ """
+ commit_hash = None
+ source = None
+
+ # Try environment variable first
+ if os.environ.get('GIT_COMMIT_HASH'):
+ commit_hash = os.environ['GIT_COMMIT_HASH'][:7]
+ source = 'env'
+ else:
+ # Try git command
+ try:
+ result = subprocess.run(
+ ['git', 'rev-parse', '--short', 'HEAD'],
+ capture_output=True,
+ text=True,
+ timeout=10
+ )
+ if result.returncode == 0:
+ commit_hash = result.stdout.strip()
+ source = 'git_cmd'
+ except (subprocess.SubprocessError, FileNotFoundError, subprocess.TimeoutExpired):
+ # Try reading from .git files
+ try:
+ git_head_file = '.git/HEAD'
+ if os.path.exists(git_head_file):
+ with open(git_head_file, 'r') as f:
+ head_content = f.read().strip()
+
+ if head_content.startswith('ref: '):
+ branch_ref = head_content[5:]
+ branch_file = os.path.join('.git', branch_ref)
+ if os.path.exists(branch_file):
+ with open(branch_file, 'r') as f:
+ commit_hash = f.read().strip()[:7]
+ source = 'git_file'
+ else:
+ commit_hash = head_content[:7]
+ source = 'git_file'
+ except (OSError, IOError):
+ pass
+
+ return {
+ 'hash': commit_hash,
+ 'available': commit_hash is not None,
+ 'source': source
+ }