Quick Start¶
Get up and running with Nauyaca in 5 minutes. This guide covers the most common use cases to help you start serving and browsing Gemini content immediately.
Prerequisites¶
- Python 3.11 or higher
- uv (recommended) or pip
Install uv (if you don't have it):
Installation¶
Choose the installation method that fits your needs:
For using Nauyaca as a library in your Python project:
Use Case 1: Serve Your First Capsule¶
Host Gemini content from your local machine in three simple steps.
1. Create Your Capsule Content¶
Create a directory with a simple Gemini text file:
mkdir my-capsule
cat > my-capsule/index.gmi << 'EOF'
# Welcome to My Gemini Capsule!
This is my first Gemini page.
## What is Gemini?
Gemini is a modern, privacy-focused protocol that sits between Gopher and the web.
=> gemini://geminiprotocol.net/ Learn more about Gemini
## Links
=> about.gmi About me
=> /blog Blog posts
Thanks for visiting!
EOF
2. Start the Server¶
You'll see output like:
[INFO] Auto-generating self-signed certificate for localhost...
[INFO] Server starting on localhost:1965
[INFO] Serving files from: /home/user/my-capsule
[INFO] Press Ctrl+C to stop
Auto-Generated Certificates
By default, Nauyaca generates a self-signed certificate for testing. This is perfect for local development. For production, see the Certificate Management Guide.
3. Test Your Server¶
Open another terminal and fetch your page:
You should see your page content displayed!
Next Steps:
- See Server Configuration for advanced options
- Learn about TLS certificate generation for production
- Read the Server Security Tutorial
Use Case 2: Browse Gemini Content¶
Fetch and view content from Gemini servers around the world.
Fetch a Public Gemini Page¶
Try fetching the official Gemini protocol homepage:
Expected Output:
# Project Gemini
## Overview
Gemini is a new internet protocol which:
* Is heavier than gopher
* Is lighter than the web
* Will not replace either
* Strives for maximum power to weight ratio
...
Trust-On-First-Use (TOFU)¶
The first time you connect to a server, Nauyaca will automatically trust and remember its certificate:
[INFO] First connection to geminiprotocol.net:1965
[INFO] Certificate fingerprint: sha256:a1b2c3d4...
[INFO] Storing certificate in TOFU database
If the certificate changes later (which could indicate a renewal or a security issue), you'll be prompted:
[ERROR] Certificate Changed!
Host: geminiprotocol.net:1965
Old fingerprint: sha256:a1b2c3d4...
New fingerprint: sha256:e5f6g7h8...
This could indicate:
1. A man-in-the-middle attack
2. Legitimate certificate renewal
To trust the new certificate, run:
nauyaca tofu trust geminiprotocol.net
View Response Details¶
Use the --verbose flag to see response headers:
Output:
Status 20 (SUCCESS)
Meta text/gemini; charset=utf-8
URL gemini://geminiprotocol.net/
# Project Gemini
...
Manage Trusted Certificates¶
# List all known hosts
nauyaca tofu list
# Manually trust a host
nauyaca tofu trust example.com
# Revoke trust (force re-trust on next connection)
nauyaca tofu revoke old-server.com
# Export TOFU database for backup
nauyaca tofu export ~/tofu-backup.toml
# Import TOFU database
nauyaca tofu import ~/tofu-backup.toml
Next Steps:
- Learn about TOFU security model
- See Client API Reference
- Explore Gemini servers to visit
Use Case 3: Use Nauyaca as a Library¶
Integrate Gemini capabilities into your Python applications.
Simple Client Example¶
Fetch and process Gemini content programmatically:
import asyncio
from nauyaca.client import GeminiClient
async def main():
# Create client with TOFU validation (recommended)
async with GeminiClient() as client:
response = await client.get("gemini://geminiprotocol.net/")
# Check response status
if response.is_success():
print(f"Content-Type: {response.meta}")
print(f"Body length: {len(response.body)} bytes")
print("\nContent:")
print(response.body)
elif response.is_redirect():
print(f"Redirected to: {response.meta}")
else:
print(f"Error {response.status}: {response.meta}")
if __name__ == "__main__":
asyncio.run(main())
Run it:
Output:
Simple Server Example¶
Create a basic Gemini server programmatically:
import asyncio
from pathlib import Path
from nauyaca.server.config import ServerConfig
from nauyaca.server.server import start_server
async def main():
# Configure server
config = ServerConfig(
host="localhost",
port=1965,
document_root=Path("./my-capsule"),
# Optional: specify certificate files
# certfile=Path("./certs/cert.pem"),
# keyfile=Path("./certs/key.pem"),
)
# Start server (runs until interrupted)
print(f"Starting server on {config.host}:{config.port}")
print(f"Serving files from: {config.document_root}")
print("Press Ctrl+C to stop")
await start_server(config)
if __name__ == "__main__":
asyncio.run(main())
Run it:
The server will auto-generate a certificate and start serving content from ./my-capsule.
Advanced Client Usage¶
Handle different response types and errors:
import asyncio
from nauyaca.client import GeminiClient
from nauyaca.security.tofu import CertificateChangedError
async def fetch_with_error_handling(url: str):
try:
async with GeminiClient(timeout=30, max_redirects=5) as client:
response = await client.get(url)
# Success (2x status codes)
if response.is_success():
return response.body
# Redirect (3x status codes)
elif response.is_redirect():
print(f"Redirected to: {response.meta}")
return await fetch_with_error_handling(response.meta)
# Input required (1x status codes)
elif 10 <= response.status < 20:
print(f"Input requested: {response.meta}")
user_input = input("Enter input: ")
# Re-request with query parameter
return await fetch_with_error_handling(f"{url}?{user_input}")
# Temporary failure (4x status codes)
elif 40 <= response.status < 50:
print(f"Temporary error: {response.meta}")
return None
# Permanent failure (5x status codes)
elif 50 <= response.status < 60:
print(f"Permanent error: {response.meta}")
return None
# Certificate required (6x status codes)
elif 60 <= response.status < 70:
print(f"Client certificate required: {response.meta}")
return None
except CertificateChangedError as e:
print(f"Certificate changed for {e.hostname}:{e.port}")
print(f"Old: {e.old_fingerprint}")
print(f"New: {e.new_fingerprint}")
return None
except TimeoutError:
print(f"Request timed out")
return None
except ConnectionError as e:
print(f"Connection failed: {e}")
return None
# Use the function
asyncio.run(fetch_with_error_handling("gemini://geminiprotocol.net/"))
Next Steps:
- Read the API Reference
- See Server Configuration for advanced options
- Learn about Rate Limiting and Access Control
- Explore Client Certificate Authentication
Common Next Steps¶
Now that you've tried the basics, here are suggested learning paths:
For Server Operators¶
- Generate Production Certificates - Create proper TLS certificates
- Configure Your Server - Set up TOML configuration
- Enable Security Features - Rate limiting, access control
- Server Configuration - Production server setup
For Client Users¶
- Understand TOFU - Learn the trust model
- Explore Geminispace - Find interesting capsules
- Use Client Certificates - Authenticate to servers
- Build Your Own Client - Custom Gemini browsers
For Developers¶
- API Reference - Complete API documentation
- Protocol Details - Understand the Gemini protocol
- Rate Limiting Guide - Configure rate limiting
- Contributing Guide - Join the project
Getting Help¶
- Documentation: Browse this site for detailed guides and references
- Examples: Check the examples/ directory
- Issues: Report bugs at GitHub Issues
- Discussions: Ask questions at GitHub Discussions
What's Next?¶
You've successfully:
- Served your first Gemini capsule
- Browsed Gemini content with TOFU validation
- Used Nauyaca as a Python library
Ready to dive deeper? Check out the Tutorials section for comprehensive guides, or jump to the How-To Guides for specific tasks.
Happy exploring Geminispace!