A complete full-stack blog platform with AI writing assistance, interactive reader modes, embedded quizzes/polls, threaded discussions, and real-time engagement.
| Feature | Medium | Substack | Inkwell |
|---|---|---|---|
| Reader Modes (3 modes) | β | β | β |
| Embedded Quizzes | β | β | β |
| Real-time Polls | β | β | β |
| ELI5 Simplification | β | β | β |
| AI Title + Tag Suggest | Basic | β | β |
| Response Blogs | β | β | β |
| Runnable Code Blocks | β | β | β |
| Topic Following | Tags only | β | β |
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββ
β React Frontend ββββββΆβ Express API ββββββΆβ MongoDB β
β (Vite + CSS βββββββ + Socket.io βββββββ (Atlas or β
β Modules) β β + AI Service β β local) β
β Port 3000 β β Port 5000 β β Port 27017 β
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββ
β
ββββββββΌβββββββ
β OpenAI β
β GPT-4o API β
βββββββββββββββ
inkwell/
βββ frontend/
β βββ src/
β β βββ components/
β β β βββ ui/ # Layout, PostCard
β β β βββ reader/ # BlockRenderer, ReaderModeToggle, ELI5Popover
β β β βββ community/ # CommentSection (threaded)
β β βββ pages/
β β β βββ HomePage # Feed with For You / Latest tabs
β β β βββ PostPage # Reading with 3 modes + interactions
β β β βββ WritePage # Block editor with AI assist
β β β βββ AuthPage # Login / Register
β β β βββ ProfilePage # Author profile + follow
β β β βββ TopicPage # Topic feed + follow
β β β βββ ExplorePage # Browse all topics
β β β βββ BookmarksPage # Saved posts
β β βββ store/ # Zustand auth store
β β βββ utils/api.js # Axios + all API calls
βββ backend/
β βββ src/
β β βββ models/ # User, Post, Comment, Topic, QuizResponse
β β βββ routes/ # auth, posts, comments, topics, users, feed, ai
β β βββ middleware/ # JWT auth
β β βββ services/
β β β βββ aiService.js # GPT-4o calls (summaries, titles, tags, ELI5)
β β βββ index.js # Express + Socket.io server
β β βββ seed.js # Demo data seeder
βββ deploy/
βββ setup.sh # EC2 one-click deploy
- Node.js 20+
- MongoDB (local or Atlas URI)
- OpenAI API key (optional β platform works without it, AI features use fallbacks)
git clone <your-repo>
cd inkwell
# Install backend deps
cd backend && npm install
# Install frontend deps
cd ../frontend && npm installcd backend
cp .env.example .env
# Edit .env β at minimum set MONGO_URI if not using local MongoDB
# Add OPENAI_API_KEY for real AI featurescd backend
node src/seed.js
# Creates demo user: demo@inkwell.io / demo1234
# Creates topics and a sample postcd backend
npm run dev # nodemon, auto-restarts
# or
npm startcd frontend
npm run dev
# Opens at http://localhost:3000
# API proxied to http://localhost:5000For a containerised local run, the repo includes docker-compose.yml.
mongodbon27017with a persistent Docker volumeinkwell-backendon5000inkwell-frontendon3000mapped to Nginx inside the container on8080
- MongoDB:
db.adminCommand('ping').ok - Backend:
http://localhost:5000/api/v1/health - Frontend waits for the backend container to become healthy before starting
docker compose up --build- Backend container defaults to
MONGO_URI=mongodb://mongodb:27017/inkwell_db - Production-sensitive values such as
JWT_SECRET,FRONTEND_URL, andOPENAI_API_KEYare read from environment variables - The backend exposes
/api/v1/health, which is also reused by Kubernetes probes and ALB health checks
- EC2 instance: Ubuntu 22.04+, t3.small or larger
- Security Group: inbound 22 (SSH), 80 (HTTP)
# From your local machine
scp -r -i your-key.pem ./inkwell ubuntu@<EC2_IP>:~/inkwell
# SSH in
ssh -i your-key.pem ubuntu@<EC2_IP>
# Run setup
cd ~/inkwell
chmod +x deploy/setup.sh
./deploy/setup.shAfter deploy: Edit backend/.env to add your OPENAI_API_KEY, then pm2 restart inkwell-api.
terraform/provisions VPC + EKS cluster (Auto Mode)backend/Dockerfileandfrontend/Dockerfilebuild production imagesk8s/contains app manifests for namespace, deployments, services, and secretsk8s/ingressclass-alb.yaml+k8s/ingress.yamlexpose the app via AWS ALB Ingressdeploy/eks-deploy.shbuilds/pushes images to ECR and deploys to EKS
- AWS CLI authenticated to your account
- Docker installed and running
kubectlinstalled- Existing EKS cluster (for this repo, defaults are in
terraform/terraform.tfvars)
cd terraform
terraform init
terraform applykubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/secret.example.yaml
kubectl edit secret inkwell-secrets -n inkwellSet at least:
MONGO_URI(recommended: MongoDB Atlas URI)JWT_SECRET(long random value)OPENAI_API_KEY(optional)FRONTEND_URL(recommended for production):- Example:
http://app.example.com,https://app.example.com - Leave empty only for temporary open CORS during setup/testing
- Example:
chmod +x deploy/eks-deploy.sh
./deploy/eks-deploy.sh <aws-region> <aws-account-id> <cluster-name>kubectl -n inkwell get pods,svc,ingress
kubectl -n inkwell get ingress inkwell-ingress -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'; echoOpen: http://<ingress-hostname>
Notes:
- Frontend and backend services remain
ClusterIP(internal). - Public access is through ALB Ingress (
/-> frontend,/api+/socket.io-> backend). - For local-only testing, you can still use:
kubectl -n inkwell port-forward svc/inkwell-frontend 8080:80
This repository currently implements the build-and-deploy path, but it does not include a committed GitHub Actions, GitLab CI, or Jenkins pipeline file yet. In other words, the delivery pipeline exists as infrastructure code, Dockerfiles, Kubernetes manifests, and deploy scripts, while orchestration is still manual.
| Stage | Implementation in repo | Details |
|---|---|---|
| Source | frontend/, backend/, k8s/, terraform/, deploy/ |
Full-stack app plus infra and deploy assets are version-controlled together |
| Build | frontend/Dockerfile, backend/Dockerfile |
Backend builds a Node 20 Alpine runtime image; frontend builds with Vite and serves from Nginx 1.27 Alpine |
| Local validation | npm run dev, npm run build, docker compose up --build |
The repo supports local app testing and container smoke testing |
| Image registry | ECR via deploy/eks-deploy.sh and Terraform ECR resources |
Backend and frontend images are tagged and pushed as latest |
| Infrastructure provisioning | terraform/ |
Creates VPC, subnets, NAT gateway, EKS Auto Mode cluster, and ECR repositories |
| Release/deploy | deploy/eks-deploy.sh |
Applies namespace, validates secrets, deploys services, deployments, ingress class, and ingress |
| Runtime routing | k8s/ingress.yaml |
AWS ALB exposes /, /api, and /socket.io |
| Health monitoring | App route + probes | Backend health endpoint is /api/v1/health; both deployments define readiness and liveness probes |
terraform applyprovisions the AWS network, EKS cluster, and ECR repositories.kubectl apply -f k8s/namespace.yamlprepares theinkwellnamespace.kubectl apply -f k8s/secret.example.yamlcreates the application secret, then values are replaced with real production secrets.deploy/eks-deploy.shlogs into ECR, builds the backend and frontend images, and pushes them to ECR.- The same script updates kubeconfig, applies deployments and services, then provisions ALB ingress exposure.
- The script waits for the ingress hostname and prints the public endpoint when the ALB is ready.
- Code is copied to the EC2 instance.
deploy/setup.shinstalls Node.js 20, MongoDB 7, Nginx, and PM2.- The backend production dependencies are installed and a default
.envis generated if missing. - The frontend is built with Vite into
frontend/dist. - Nginx serves the frontend and reverse-proxies
/apiand/socket.ioto the Node backend on port5000. - PM2 starts the backend as
inkwell-apiand persists startup configuration.
- Non-root container execution in both production Docker images
- ECR
scan_on_push = trueconfigured through Terraform - Kubernetes secrets separated from deployment manifests
- ALB ingress with explicit health check path
- Backend rate limiting and configurable CORS allowlist
- EKS control plane logging enabled for
api,audit,authenticator,controllerManager, andscheduler - Kubernetes secret encryption at rest enabled in the cluster configuration
- No committed CI workflow file for automatic linting, testing, image publishing, or deployment on push
- No automated test scripts are defined in
backend/package.jsonorfrontend/package.json - Container images are pushed with the mutable
latesttag only - Secrets are created manually before EKS deployment
- No rollback script, promotion workflow, or separate staging/production environments are defined in the repo
The project is deployment-ready in two forms:
- A VM-style deployment on AWS EC2 using Nginx, PM2, and local MongoDB
- A containerised Kubernetes deployment on AWS EKS using Terraform, ECR, Kubernetes manifests, and ALB ingress
| Area | Status | Evidence |
|---|---|---|
| Application containerisation | Implemented | backend/Dockerfile, frontend/Dockerfile, docker-compose.yml |
| Local multi-container deployment | Implemented | docker-compose.yml with MongoDB, backend, frontend |
| EC2 deployment automation | Implemented | deploy/setup.sh |
| EKS infrastructure provisioning | Implemented | terraform/main.tf plus variables and outputs |
| Kubernetes workloads and services | Implemented | k8s/backend.deployment.yaml, k8s/frontend.deployment.yaml, services, ingress |
| Public ingress | Implemented | k8s/ingressclass-alb.yaml, k8s/ingress.yaml |
| Secret management pattern | Partially implemented | k8s/secret.example.yaml exists, but real secret injection is manual |
| Automated CI/CD orchestration | Not yet implemented in repo | No .github/workflows/, .gitlab-ci.yml, or Jenkinsfile present |
| Automated testing gate | Not yet implemented in repo | No test scripts in the app packages |
- Frontend runs as a static Vite build served by Nginx.
- Backend runs as a Node.js 20 Express service with Socket.IO.
- For Kubernetes, both frontend and backend run with
2replicas and internalClusterIPservices. - External traffic is routed through an AWS ALB ingress:
/-> frontend,/api-> backend,/socket.io-> backend. - Health checking is consistent across environments through
/api/v1/health. - Data persistence differs by target: EC2 uses local MongoDB installed on the instance, while EKS is designed to use a secret-provided MongoDB connection string and also includes a single-file manifest with an in-cluster MongoDB option.
- Add a real CI workflow that runs install, build, container build, and smoke checks on every pull request.
- Add automated tests and wire them into the CI gate before image publication.
- Tag images with commit SHA or semantic versions instead of only
latest. - Move secret creation to a managed workflow such as AWS Secrets Manager, External Secrets, or sealed secrets.
- Add separate staging and production environments with controlled promotion.
The write page uses a custom block editor with 7 block types:
- Paragraph β rich text
- Heading β H1, H2, H3
- Code β with language select and optional "Run" button (JS only in browser)
- Quiz β MCQ with correct answer tracking and aggregate stats
- Poll β real-time voting via Socket.io
- Callout β info/tip/warning/danger
- Divider
- Title suggestions β click "AI Titles" in the write toolbar
- Auto-tagging β click "AI Tags" to generate relevant tags from content
- Summaries β generated on publish, powers Quick Read and Exam Mode
- ELI5 β select any text in a post, get a simplified explanation in a popover
Toggle via the sticky pill on any post:
- Quick Read β AI-generated 2-3 sentence summary
- Deep Dive β full content with all interactive blocks
- Exam Mode β bullet points, revision notes, key concepts
- Threaded comments (2 levels deep)
- Comment likes
- AI-ranked "Top Insights" shown at the top
- Response blogs β write a full post in response to another
- Author follow + topic follow
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/auth/register |
Register user |
| POST | /api/v1/auth/login |
Login, get JWT |
| GET | /api/v1/posts?topic=&author=&page= |
List posts |
| GET | /api/v1/posts/:slug?readerMode=quick|exam |
Get post (mode-aware) |
| POST | /api/v1/posts |
Create post |
| PUT | /api/v1/posts/:id |
Update post |
| POST | /api/v1/posts/:id/like |
Toggle like |
| POST | /api/v1/posts/:id/blocks/:blockId/poll-vote |
Vote on poll |
| POST | /api/v1/posts/:id/blocks/:blockId/quiz-answer |
Submit quiz answer |
| GET | /api/v1/comments/post/:postId |
Get comments (threaded) |
| POST | /api/v1/comments/post/:postId |
Post comment |
| GET | /api/v1/feed |
Personalised feed (auth) |
| POST | /api/v1/ai/suggest-titles |
AI title suggestions |
| POST | /api/v1/ai/generate-tags |
AI tag generation |
| POST | /api/v1/ai/eli5 |
Simplify text selection |
pm2 status # Check backend status
pm2 logs inkwell-api # View backend logs
pm2 restart inkwell-api # Restart backend
cd backend && node src/seed.js # Re-seed demo data
sudo systemctl reload nginx # Reload nginx config
docker compose up --build # Local container deployment
kubectl -n inkwell get pods,svc # EKS workload status
kubectl -n inkwell get ingress # EKS public endpointBuilt with β¦ by the Inkwell team. Write clearly. Think deeply.