a geicko-2 based round robin ranking system designed to test c++ battleship submissions battleship.dunkirk.sh

chore: fix padding

dunkirk.sh 3cb3b27e cdc6f7e1

verified
-167
AUTH_IMPLEMENTATION.md
···
-
# SSH Public Key Authentication - Implementation Summary
-
-
## ✅ What Was Implemented
-
-
### 1. Database Schema
-
Added `users` table to track authenticated users:
-
- `username` - SSH username (unique)
-
- `name` - Full name (required during onboarding)
-
- `bio` - Optional description
-
- `link` - Optional website/social link
-
- `public_key` - SSH public key (unique, used for auth)
-
- `created_at` - Registration timestamp
-
- `last_login_at` - Last successful login
-
-
**Location:** `internal/storage/database.go` + new `internal/storage/users.go`
-
-
### 2. SSH Authentication Handler
-
Implements public key authentication flow:
-
- Checks if public key is registered
-
- If registered: verifies username matches and allows access
-
- If new: checks if username is available
-
- If username taken: rejects (prevents key reuse)
-
- If available: flags user for onboarding
-
-
**Location:** `internal/server/auth.go`
-
-
### 3. Onboarding Flow
-
Interactive terminal prompt for first-time users:
-
- Prompts for full name (required)
-
- Prompts for bio (optional, skip with Enter)
-
- Prompts for link (optional, skip with Enter)
-
- Creates user record with their public key
-
- Subsequent logins skip onboarding
-
-
**Location:** `internal/server/auth.go` + `internal/tui/onboarding.go`
-
-
### 4. User Profile Pages
-
Web interface to view user information:
-
- `/users` - List all registered users
-
- `/user/{username}` - Individual user profile showing:
-
- Name, bio, and link
-
- SSH public key fingerprint
-
- Game statistics (rating, wins, losses)
-
- Join date and last login
-
-
**Location:** `internal/server/users.go`
-
-
### 5. Leaderboard Integration
-
Updated leaderboard to link usernames to profiles:
-
- Clicking a username takes you to their profile
-
- Shows authentication info alongside game stats
-
-
**Location:** `internal/server/web.go` (updated player name links)
-
-
## 🔐 Security Features
-
-
1. **Public key only** - No password authentication accepted
-
2. **Username ownership** - One public key per username, cannot be changed
-
3. **Key uniqueness** - One public key cannot register multiple usernames
-
4. **Automatic verification** - Every connection validates the key
-
-
## 📝 User Experience
-
-
### First Connection
-
```bash
-
ssh -p 2222 -i ~/.ssh/id_ed25519 alice@localhost
-
```
-
-
**Prompts:**
-
```
-
🚢 Welcome to Battleship Arena!
-
Setting up account for: alice
-
-
What's your full name? (required): Alice Johnson
-
Bio (optional, press Enter to skip): CS student and battleship enthusiast
-
Link (optional, press Enter to skip): https://github.com/alice
-
-
✅ Account created successfully!
-
You can now upload your battleship AI and compete!
-
```
-
-
### Subsequent Connections
-
```bash
-
ssh -p 2222 alice@localhost
-
# → Immediately shows TUI dashboard (no prompts)
-
```
-
-
### Uploading Files
-
```bash
-
scp -P 2222 memory_functions_alice.cpp alice@localhost:~/
-
# → Works with same key authentication
-
```
-
-
## 🌐 Web Interface
-
-
### User List (`/users`)
-
- Grid view of all registered users
-
- Shows name, username, bio
-
- Click to view full profile
-
-
### User Profile (`/user/alice`)
-
- Full name and username
-
- Bio and external link (if provided)
-
- SSH public key fingerprint (SHA256)
-
- Game statistics (if they've competed)
-
- Registration and last login timestamps
-
-
### Leaderboard (`/`)
-
- Usernames are now clickable links
-
- Lead to user profile pages
-
- Shows rating, wins, losses, etc.
-
-
## 📂 Files Modified/Created
-
-
### New Files
-
- `internal/storage/users.go` - User CRUD operations
-
- `internal/server/auth.go` - SSH authentication handlers
-
- `internal/server/users.go` - User profile web handlers
-
- `internal/tui/onboarding.go` - Onboarding TUI (Bubble Tea model)
-
- `SSH_AUTH.md` - User-facing documentation
-
-
### Modified Files
-
- `internal/storage/database.go` - Added users table to schema
-
- `cmd/battleship-arena/main.go` - Added auth handlers and user routes
-
- `internal/server/web.go` - Updated player name links to /user/
-
-
## 🚀 Testing
-
-
1. **Start server:**
-
```bash
-
make run
-
```
-
-
2. **Connect with new user:**
-
```bash
-
ssh -p 2222 newuser@localhost
-
```
-
-
3. **View users:**
-
```
-
http://localhost:8081/users
-
```
-
-
4. **View profile:**
-
```
-
http://localhost:8081/user/newuser
-
```
-
-
5. **Try duplicate username:**
-
```bash
-
# With different SSH key, same username → should be rejected
-
```
-
-
## 💡 Design Decisions
-
-
1. **Onboarding in terminal** - Users are already in SSH, so keep it simple
-
2. **Public key as primary key** - Ensures one key = one account
-
3. **Optional bio/link** - Don't force users to provide info they don't want to share
-
4. **SHA256 fingerprint display** - More readable than full public key
-
5. **Separate /user/ route** - Distinguishes from game stats at /player/
-
-
## 🔄 Migration Path
-
-
Existing deployments will need to:
-
1. Run migration to add users table (happens automatically on next startup)
-
2. Existing SSH users will be prompted for onboarding on next login
-
3. No data loss - submission history remains intact
-60
SSH_AUTH.md
···
-
# SSH Public Key Authentication
-
-
The Battleship Arena now uses SSH public key authentication for secure, passwordless access.
-
-
## First-Time Setup
-
-
1. **Generate an SSH key** (if you don't have one):
-
```bash
-
ssh-keygen -t ed25519 -f ~/.ssh/battleship_arena
-
```
-
-
2. **Connect for the first time**:
-
```bash
-
ssh -p 2222 -i ~/.ssh/battleship_arena yourname@localhost
-
```
-
-
3. **Complete onboarding**:
-
- Enter your full name (required)
-
- Enter a bio (optional)
-
- Enter a website/link (optional)
-
-
4. **Your public key is now registered!** Only you can access this username.
-
-
## Uploading Your AI
-
-
After registration, upload your battleship AI:
-
-
```bash
-
scp -P 2222 -i ~/.ssh/battleship_arena memory_functions_yourname.cpp yourname@localhost:~/
-
```
-
-
## User Profiles
-
-
- View your profile: `https://arena.example.com/user/yourname`
-
- View all users: `https://arena.example.com/users`
-
- Profiles display:
-
- Name, bio, and link
-
- SSH public key fingerprint
-
- Game statistics (if you've competed)
-
-
## Security Features
-
-
- ✅ Public key authentication only (no passwords)
-
- ✅ Username ownership tied to SSH key
-
- ✅ Keys cannot be reused for different usernames
-
- ✅ Automatic key verification on every connection
-
-
## SSH Config
-
-
Add to `~/.ssh/config` for easy access:
-
-
```
-
Host battleship
-
HostName localhost
-
Port 2222
-
User yourname
-
IdentityFile ~/.ssh/battleship_arena
-
```
-
-
Then simply: `ssh battleship`
-95
STRUCTURE.md
···
-
# Battleship Arena - Code Structure
-
-
Refactored into a clean modular architecture with proper separation of concerns.
-
-
## Directory Structure
-
-
```
-
battleship-arena/
-
├── cmd/
-
│ └── battleship-arena/ # Main application entry point
-
│ └── main.go # Server initialization and routing
-
├── internal/ # Private application code
-
│ ├── runner/ # Match execution and compilation
-
│ │ ├── runner.go # AI compilation and match running
-
│ │ └── worker.go # Background submission processor
-
│ ├── server/ # HTTP/SSH server components
-
│ │ ├── scp.go # SCP file upload handler
-
│ │ ├── sftp.go # SFTP file upload handler
-
│ │ ├── sse.go # Server-Sent Events for live updates
-
│ │ └── web.go # HTTP handlers and HTML templates
-
│ ├── storage/ # Data persistence layer
-
│ │ ├── database.go # SQLite schema and queries
-
│ │ └── tournament.go # Tournament bracket management
-
│ └── tui/ # Terminal User Interface
-
│ └── model.go # Bubble Tea SSH interface
-
├── battleship-engine/ # C++ battleship game engine
-
├── static/ # Static web assets
-
├── go.mod # Go module definition
-
└── Makefile # Build automation
-
-
```
-
-
## Module Responsibilities
-
-
### `cmd/battleship-arena`
-
- Application entry point
-
- Server initialization (SSH, HTTP, SSE)
-
- Dependency injection and configuration
-
- Graceful shutdown handling
-
-
### `internal/runner`
-
- **runner.go**: Compiles C++ submissions, generates match binaries, runs head-to-head games
-
- **worker.go**: Background worker that processes pending submissions in a queue
-
-
### `internal/server`
-
- **scp.go**: Validates and handles SCP file uploads from students
-
- **sftp.go**: SFTP subsystem for file uploads
-
- **sse.go**: Server-Sent Events for real-time leaderboard updates and progress tracking
-
- **web.go**: HTTP handlers for leaderboard, player pages, and API endpoints
-
-
### `internal/storage`
-
- **database.go**: SQLite schema, CRUD operations, Glicko-2 rating system implementation
-
- **tournament.go**: Bracket generation, seeding, match scheduling, winner advancement
-
-
### `internal/tui`
-
- **model.go**: Bubble Tea terminal interface shown when students SSH in
-
-
## Key Design Decisions
-
-
1. **Internal packages**: Use `internal/` to prevent external imports and keep APIs private
-
2. **Dependency injection**: Pass configuration (uploadDir, ports) through function parameters rather than globals
-
3. **Clean interfaces**: Each module exports only what's needed (capital letters for public functions)
-
4. **Separation of concerns**: Storage, presentation, business logic, and transport are cleanly separated
-
5. **No circular dependencies**: Dependencies flow downward (cmd → server/runner → storage)
-
-
## Building & Running
-
-
```bash
-
# Build binary
-
make build
-
-
# Run server
-
make run
-
-
# Generate SSH host key
-
make gen-key
-
-
# Clean artifacts
-
make clean
-
```
-
-
## Adding Features
-
-
- **New API endpoint**: Add handler to `internal/server/web.go`, register route in `cmd/battleship-arena/main.go`
-
- **New database table**: Update schema in `storage.InitDB()`, add query functions to `internal/storage/database.go`
-
- **New match logic**: Modify `internal/runner/runner.go`
-
- **New TUI screen**: Update model in `internal/tui/model.go`
-
-
## Testing
-
-
```bash
-
go test ./...
-
```
-
-
Currently no tests exist (all packages return `[no test files]`), but the modular structure makes it easy to add unit tests for each package.
battleship-arena

This is a binary file and will not be displayed.

+1 -1
internal/server/auth.go
···
func SetConfig(passcode, url string) {
adminPasscode = passcode
externalURL = url
-
log.Printf("✓ Config loaded: passcode=%s..., url=%s", passcode[:10], url)
+
log.Printf("✓ Config loaded: url=%s\n", url)
}
func PublicKeyAuthHandler(ctx ssh.Context, key ssh.PublicKey) bool {
+13 -13
internal/tui/model.go
···
b.WriteString(lipgloss.NewStyle().Bold(true).Render("📤 Your Submissions") + "\n\n")
headerStyle := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("240"))
-
b.WriteString(headerStyle.Render(fmt.Sprintf("%-35s %-15s %s\n",
-
"Filename", "Uploaded", "Status")))
+
b.WriteString(headerStyle.Render(fmt.Sprintf("%-35s %-15s %s",
+
"Filename", "Uploaded", "Status")) + "\n")
for _, sub := range submissions {
var statusColor string
···
relTime := formatRelativeTime(sub.UploadTime)
-
// Format the line without styles first for proper alignment
+
// Build the line manually to avoid formatting issues with ANSI codes
+
line := fmt.Sprintf("%-35s %-15s ", sub.Filename, relTime)
statusStyled := lipgloss.NewStyle().Foreground(lipgloss.Color(statusColor)).Render(sub.Status)
-
b.WriteString(fmt.Sprintf("%-35s %-15s %s\n",
-
sub.Filename, relTime, statusStyled))
+
b.WriteString(line + statusStyled + "\n")
}
return b.String()
···
for i, entry := range entries {
rank := fmt.Sprintf("#%d", i+1)
-
// Apply color only to the rank
-
var coloredRank string
+
// Apply color only to the rank and pad manually
+
var displayRank string
if i == 0 {
-
coloredRank = lipgloss.NewStyle().Foreground(lipgloss.Color("220")).Render(rank) // Gold
+
displayRank = lipgloss.NewStyle().Foreground(lipgloss.Color("220")).Render(rank) + " " // Gold
} else if i == 1 {
-
coloredRank = lipgloss.NewStyle().Foreground(lipgloss.Color("250")).Render(rank) // Silver
+
displayRank = lipgloss.NewStyle().Foreground(lipgloss.Color("250")).Render(rank) + " " // Silver
} else if i == 2 {
-
coloredRank = lipgloss.NewStyle().Foreground(lipgloss.Color("208")).Render(rank) // Bronze
+
displayRank = lipgloss.NewStyle().Foreground(lipgloss.Color("208")).Render(rank) + " " // Bronze
} else {
-
coloredRank = rank
+
displayRank = fmt.Sprintf("%-4s", rank)
}
// Format line with Glicko-2 rating ± RD
ratingStr := fmt.Sprintf("%d±%d", entry.Rating, entry.RD)
-
b.WriteString(fmt.Sprintf("%-4s %-20s %11s %8d %8d %9.2f%% %9.1f\n",
-
coloredRank, entry.Username, ratingStr, entry.Wins, entry.Losses, entry.WinPct, entry.AvgMoves))
+
b.WriteString(fmt.Sprintf("%s %-20s %11s %8d %8d %9.2f%% %9.1f\n",
+
displayRank, entry.Username, ratingStr, entry.Wins, entry.Losses, entry.WinPct, entry.AvgMoves))
}
return b.String()