A lightweight, serverless note-taking application written in Go. Create, edit, and share note with auto-save functionality. Deploy to AWS Lambda with S3 storage or run locally with disk-based storage.
- 📝 Simple Note Editor: Lightweight web interface for creating and editing note
- 💾 Auto-Save: Automatically saves note content every second
- 🔗 Shareable URLs: Note is accessible via direct links with unique IDs
- 🖨️ Print Support: Print-friendly interface for saving note
- ⌨️ TAB Support: TAB key works for indentation instead of moving focus
- 🚀 Multi-Deployment: HTTP server, Docker container, or AWS Lambda
- 💾 Flexible Storage: Local disk or AWS S3 backend
- 🔒 Secure: Input validation and XSS protection
Requirements:
- Go 1.24+
Run:
go run main.goOpen http://localhost:8080 in your browser.
With custom settings:
export PORT=3000
export NOTE_DIR=./my-note
go run main.goBuild:
docker build -t note-app .Run:
docker run -p 8080:8080 -v ./note:/note note-appThen open http://localhost:8080
Prerequisites:
- AWS account with IAM permissions
- S3 bucket for storing note
Deploy with SAM (recommended):
sam build
sam deploy --guidedManual deployment:
-
Build binary for Lambda:
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o bootstrap main.go zip function.zip bootstrap
-
Create Lambda function via AWS Console
-
Set environment variables:
S3_BUCKET: Your S3 bucket nameS3_PREFIX: "note" (default)AWS_REGION: "us-east-1" (default)
-
Upload ZIP file to Lambda
PORT: HTTP server port (default:8080)NOTE_DIR: Directory to store note (default:/note)URL: Optional - Public URL for sharing note (e.g.,https://note.example.com). If not set, the domain is auto-detected from the request. Useful for reverse proxies where auto-detection may not work correctly.
S3_BUCKET: Required - S3 bucket name for storing noteS3_PREFIX: S3 object key prefix (default:note)AWS_REGION: AWS region (default:us-east-1)
Runtime detection is automatic:
- If
AWS_LAMBDA_FUNCTION_NAMEis set → Lambda mode with S3 storage - Otherwise → HTTP server mode with local storage
Retrieve and display a note.
Parameters:
noteId(path) ornote(query): Note ID (alphanumeric, 5-20 chars)
Notes:
- The preferred URL format is now
/noteid/{noteId}for shell-friendly links (e.g.,http://example.com/noteid/ABCDE). - Backwards compatibility:
/?note={noteId}still works. - The server also supports being mounted under a reverse-proxy subpath (e.g.,
https://example.com/app/); links and copied URLs will preserve the subpath.
Response:
- HTML page with note content in a textarea
- If note doesn't exist, returns empty textarea
Example:
# Preferred path-style URL (no ? character)
curl http://localhost:8080/noteid/abc12
# Legacy query-style URL (still supported)
curl http://localhost:8080/?note=abc12Save or delete a note.
Request body (JSON):
{
"noteId": "abc12",
"content": "Note content here"
}Response (JSON):
{
"success": true,
"noteId": "abc12"
}Behavior:
- If
noteIdis empty, a random 5-character ID is generated - If
contentis empty, the note is deleted - Otherwise, the note is saved
Example:
# Create new note
curl -X POST http://localhost:8080/ \
-H "Content-Type: application/json" \
-d '{"content":"Hello World"}'
# Update note
curl -X POST http://localhost:8080/ \
-H "Content-Type: application/json" \
-d '{"noteId":"abc12","content":"Updated content"}'
# Delete note
curl -X POST http://localhost:8080/ \
-H "Content-Type: application/json" \
-d '{"noteId":"abc12","content":""}'go build -o note-app main.go
./note-appgo build -ldflags \
"-X main.Version=v1.0.0 \
-X main.BuildTime=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
-X main.CommitHash=$(git rev-parse --short HEAD)" \
-o note-appGOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build \
-ldflags="-X main.Version=v1.0.0" \
-o bootstrap main.go
zip function.zip bootstrapdocker build -t note-app:latest .go test -v ./...go test -cover ./...go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out.
├── main.go # Entry point and runtime detection
├── handlers.go # HTTP request handlers
├── storage_local.go # Local file storage implementation
├── storage_s3.go # AWS S3 storage implementation
├── lambda.go # AWS Lambda handler and API Gateway support
├── utils.go # Utility functions
├── *_test.go # Unit tests
├── go.mod # Go module definition
├── go.sum # Dependency checksums
├── Dockerfile # Container build definition
├── .github/
│ └── workflows/
│ ├── test.yml # Run tests on push/PR
│ ├── build-container.yml # Build and push Docker image
│ └── deploy-lambda.yml # Deploy to AWS Lambda
└── README.md # This file
test.yml:
- Runs on every push and pull request
- Executes
go test -v -cover ./... - Runs
gofmtandgo vetchecks - Uploads coverage to Codecov
build-container.yml:
- Triggers on push to
mainand version tags - Builds and pushes Docker image to GitHub Container Registry and Docker Hub
- Requires Docker Hub credentials in secrets
deploy-lambda.yml:
- Triggers on push to
mainand version tags - Builds binary for Lambda (ARM64)
- Deploys to AWS Lambda
- Requires AWS credentials configured via OIDC
For GitHub Actions workflows to work, configure these secrets in your repository:
For Docker build:
DOCKER_USERNAME: Docker Hub usernameDOCKER_PASSWORD: Docker Hub access token
For Lambda deployment:
AWS_ROLE_TO_ASSUME: AWS IAM role ARN for OIDCAWS_REGION: AWS region (optional, defaults to us-east-1)
- ✅ Input Validation: Note IDs are alphanumeric only
- ✅ XSS Protection: User content is HTML-escaped
- ✅ IAM Security: Lambda uses IAM roles, no hardcoded credentials
- ✅ HTTPS Ready: Works behind reverse proxies with TLS
- Cold Start (Lambda): ~500ms (Go is fast!)
- Auto-save: 1-second polling interval
- Concurrent Users: Scales automatically in Lambda mode
- Storage: O(1) for read/write operations
Ensure the NOTE_DIR directory exists and is writable:
mkdir -p /note
chmod 755 /note- Verify S3 bucket exists and is accessible
- Check Lambda execution role has S3 permissions
- Ensure
S3_BUCKETenvironment variable is set - Check CloudWatch logs for errors
Change port with:
export PORT=3000
go run main.goMIT License - feel free to use and modify for your needs.
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Submit a pull request
When deployed, the app prints version information at startup:
Note App - Version: {Version}, BuildTime: {BuildTime}, CommitHash: {CommitHash}
This information is injected at build time via ldflags.