GitHub

Orcta Internal Docs

Welcome to the internal documentation hub for Orcta Technologies. These guides capture who we are, what we build, and how we deliver work so teams can move fast with shared context.

Start Here

  • Company - Mission, story, and values that guide decisions
  • Products - Current product portfolio and product overviews
  • Services - What we offer clients and how we deliver
  • Engineering - Workflow, standards, and review practices
  • Infrastructure - Deployment, VPS, and production runbooks
  • Insights - Philosophies, quotes, and short internal posts

How to Use These Docs

Each section is meant to be short, actionable, and updated as we evolve. If you spot gaps or outdated details, open a PR with the change and include a brief rationale.

Edit this page

Company

Core context about Orcta Technologies for internal alignment and external consistency.

Edit this page

Mission & Positioning

Orcta exists to bridge innovation and humanity. We transform complex challenges into reliable, well-designed products that serve real users and evolve with purpose.

Positioning statement: Orcta Technologies designs and engineers thoughtful digital and embedded systems that balance performance, reliability, and human experience.

Edit this page

Company Story

Orcta Technologies is a Ghana-based technology company focused on practical, sustainable solutions across software, hardware, electronics, and robotics. We started as a small team of thinkers and builders who believed technology should understand people, not the other way around.

Since 2019, our team has delivered innovative and scalable systems for clients worldwide. By combining software and embedded systems under one roof, we can build complete ecosystems where apps, devices, and users connect seamlessly.

Edit this page

Values & Principles

  • Empathy first: design and build for real people and real contexts.
  • Reliability by default: systems must be stable, predictable, and resilient.
  • Performance with purpose: optimize for outcomes that matter, not vanity metrics.
  • Design with intent: clear UX and thoughtful interactions are non-negotiable.
  • Trust at the core: we earn long-term partnerships through transparency and follow-through.
Edit this page

Products

Current product portfolio and internal product summaries.

Edit this page

KNUST Career App

A centralized digital platform designed to support KNUST students and alumni throughout their career journey, from skills development to job placement.

Core Capabilities

  • Personalized opportunities for jobs, internships, scholarships, and National Service.
  • Student profiles with academic background, skills, certifications, and goals.
  • Dashboard for career events, applications, and progress tracking.
  • Built-in chatbot for instant guidance and common questions.
  • Mentorship, resume building, and skill development tools to improve readiness.

Primary Users

  • Students and alumni seeking opportunities and career support.
  • Career services teams managing programs, events, and guidance.
Edit this page

Orcta Go

A smart mobility platform that simplifies daily transportation by enabling pre-booked shared rides and commercial buses with predictable pricing and schedules.

Core Capabilities

  • Seat reservations in advance for shared transport.
  • Clear visibility into pickup points, routes, and schedules.
  • Predictable and affordable pricing for commuters.
  • Reduced waiting times and overcrowding through structured routing.

Primary Users

  • Daily commuters seeking reliable, low-stress transport.
  • Transport operators coordinating routes and capacity.
Edit this page

On this page

Orcta Runtime

A pull-based deployment automation platform that manages multi-app Docker Compose deployments with health verification, auto-rollback, and automatic TLS routing.

Core Capabilities

  • Multi-App Management — Register multiple apps with independent compose directories, health endpoints, and Caddy domains.
  • Pull-Based Deploys — CI pushes to GHCR → webhook fires → Orcta pulls and deploys via docker compose.
  • Health Verification — TCP/HTTP checks after deploy with auto-rollback to last known healthy SHA on failure.
  • Caddy Integration — Auto-registers reverse proxy routes with TLS on successful deploy.
  • Pre/Post-Deploy Hooks — Blocking gates (e.g., run migrations before deploy) and async notifications (Slack, Discord, custom webhooks).
  • Role-Based Auth — Admin/operator/viewer API tokens + GitHub OAuth with org membership gating.
  • React Dashboard — Monitor executions, manage apps, hooks, and cron schedules.
  • Cron Scheduling — Schedule recurring actions (backups, maintenance tasks).

Architecture

GitHub Actions → GHCR → Webhook → Orcta Runtime → docker compose pull && up -d → Caddy Route
                                                  ↓
                                            Health Check → Pass / Rollback

Key decisions:

  • Pull-based model (not push) — Orcta pulls images, CI doesn’t SSH into servers.
  • Per-app mutex locks prevent concurrent deploys to the same app.
  • Auto-rollback uses ${DEPLOY_SHA:-latest} in compose image tags.
  • Caddy admin API at localhost:2019 for automatic route management.

Primary Users

  • Engineering team deploying microservices to shared VPS.
  • CI/CD pipelines triggering deploys via GitHub webhooks.
  • Operations monitoring deploy health and rollback status.

Deployment

Orcta Runtime runs as a Docker container on the VPS:

# Deploy
docker compose pull && docker compose up -d

# Dashboard
https://runtime.orctatech.com
Edit this page

Services

Service lines offered by Orcta Technologies and how we approach delivery.

Edit this page

Digital Product Development

We design and engineer custom software solutions that help teams move faster, work smarter, and build products people enjoy using.

What We Deliver

  • Web applications and platforms that scale with business growth.
  • Secure, efficient systems that simplify operations.
  • End-to-end delivery from discovery through deployment.
Edit this page

IoT & Embedded Systems

We bridge software and hardware to build complete ecosystems where devices, data, and users connect seamlessly.

What We Deliver

  • IoT integrations and device connectivity.
  • Embedded software for real-world environments.
  • Systems designed for reliability and long-term maintenance.
Edit this page

Applied AI

We build AI capabilities that support decisions, automate workflows, and integrate cleanly into existing products and operations.

What We Deliver

  • Automation and prediction features that reduce manual effort.
  • AI services designed for human-in-the-loop workflows.
  • Practical, measurable outcomes instead of novelty projects.
Edit this page

Design & UX

We design experiences that are intuitive, accessible, and built to scale as products grow.

What We Deliver

  • User-centered UI and UX design grounded in real workflows.
  • Prototypes and design systems that align teams quickly.
  • Interfaces that reinforce trust and usability across platforms.
Edit this page

Architecture Review

We assess technology stacks to uncover bottlenecks, risks, and opportunities to improve reliability and performance.

What We Deliver

  • System audits focused on scalability and resilience.
  • Recommendations for tooling, infrastructure, and code quality.
  • Clear next steps with prioritized improvements.
Edit this page

Engineering

Engineering Documentation

Everything you need to build, ship, and maintain great software at Orcta. From workflow guides to coding standards.

Quick Start Guides

Practical, actionable guides for daily development work.

Complete Documentation

In-depth resources covering all aspects of engineering at Orcta.

Why These Docs Exist

Documentation as a force multiplier for engineering teams.

Faster Onboarding

New engineers ship their first PR in days, not weeks. Clear guides eliminate guesswork.

Consistent Standards

Everyone follows the same patterns. Code reviews are faster. Codebases are more maintainable.

Shared Understanding

Documentation captures tribal knowledge. What one person learns, everyone can benefit from.

Additional Resources

Doc Index

Edit this page

On this page

Engineering Cheat Sheet

Essential principles, conventions, and decision guides for daily development work.

Version 1.2 · Last Updated July 2026 · Print for Quick Reference

Core Engineering Beliefs

Principles Over Tools

Deep understanding matters more than superficial mastery of frameworks. Concepts drive tool choices.

Progressive Abstraction

Build simple first, then abstract. Avoid premature optimization that obscures clarity.

Code as Craft

Write for humans first. Clean, understandable code shows respect for future readers.

Document to Scale

Documentation is an act of generosity to teammates and your future self.

Speed with Stability

Move fast without shipping broken things. Testing and reviews safeguard quality.

Ownership Mentality

If you ship it, you own it. Monitor, maintain, and improve what you build.

Non-Negotiables

  • Every pull request gets reviewed. No self-merges. Feedback must be kind, clear, and constructive.
  • Tests are mandatory. 80% coverage target for core services. No broken code in main branches.
  • Never commit secrets. Use environment variables and secrets managers. Check before pushing.

Naming Conventions

JavaScript / TypeScript

  • Variables: camelCase
  • Functions: camelCase
  • Components: PascalCase
  • Constants: SCREAMING_SNAKE_CASE
  • Files: kebab-case

Python

  • Variables: snake_case
  • Functions: snake_case
  • Classes: PascalCase
  • Constants: SCREAMING_SNAKE_CASE
  • Files: kebab-case

Go

  • Variables: camelCase
  • Functions: camelCase
  • Exported Types: PascalCase
  • Packages: lowercase
  • Files: kebab-case

Decision Guide

Should I ship without tests? No. Write at least unit tests for business logic. Integration tests for user-facing features.

Should I self-merge my PR? No. Get one approval minimum, unless you’re a tech lead handling an emergency hotfix.

Should I abstract this code? Only if you’ve used it three or more times. Build simple first, abstract later.

Should I skip documentation? No. At minimum: what it does, how to run it, and any known issues.

Should I deploy to production? Only after dev → staging → tech lead approval. Monitor for at least one hour after. Check Orcta Runtime dashboard at runtime.orctatech.com and LogDeck at logdeck.orctatech.com.

Should I refactor this duplicated code? Yes, now. Don’t postpone improvements. Make the codebase better every time you touch it.

Monitoring & Observability

  • Orcta Runtime: https://runtime.orctatech.com — Deployment history, health checks, rollback
  • LogDeck: https://logdeck.orctatech.com — Real-time logs, alerting, container status
  • Lookout: journalctl -u lookout -f — Server metrics (CPU, memory, disk, Docker)
  • Caddy logs: journalctl -u caddy --no-pager -n 50 — Reverse proxy access logs

Development Workflow

  1. Start with Why — Understand the user need before writing code. Link to issues or document the requirement.

  2. Design Before Code — Think through architecture first. Draw diagrams or write brief design docs for complex features.

  3. Build Simple — Start with the simplest implementation. Avoid premature optimization or abstraction.

  4. Test Thoroughly — Write tests that cover happy paths and edge cases. Run locally before pushing.

  5. Refactor as You Go — Clean up duplicated code and unclear logic immediately while context is fresh.

  6. Submit for Review — Use PR template. Include clear description, screenshots for UI, and test instructions.

  7. Address Feedback — Respond within 24 hours. Make changes or discuss alternatives. Reviews are collaborative.

  8. Monitor After Deploy — Watch logs and metrics for at least one hour. Address issues quickly or revert if needed.


These aren’t rules to slow you down—they’re guardrails to help you move faster, together.

Edit this page

On this page

Development Workflow

A systematic approach to building, reviewing, and shipping code at Orcta.

Core Workflow

1. Start with Why

Every feature begins with understanding the user need or pain point being addressed.

Before writing code, clarify the problem space. Link to the relevant issue or ticket. If none exists, document the user story or requirement first.

Reference: Engineering Philosophy, Section 3

2. Design Before Implementation

Think through architecture and approach before the first line of code.

For small changes, outline your approach in comments or notes. For larger features, write a brief design document or create a diagram. Share with the team for early feedback.

Reference: Engineering Philosophy, Section 3

3. Build Simple, Then Abstract

Start with the simplest implementation that solves the problem.

Avoid premature optimization or abstraction. If you find yourself building for hypothetical future requirements, step back and focus on the current need. Abstractions emerge naturally after patterns repeat.

Reference: Engineering Philosophy, Section 2

4. Test Your Implementation

Write tests that validate both happy paths and edge cases.

Unit tests for business logic are required. Integration tests for user-facing features are expected. Aim for 80% coverage on core services. Run tests locally before committing.

Reference: Engineering Playbook, Section 7

5. Refactor as You Go

Improve code quality incrementally rather than accumulating technical debt.

When you encounter duplicated code, unclear naming, or confusing logic, fix it immediately. The best time to refactor is when the context is fresh in your mind.

Reference: Engineering Philosophy, Section 3

6. Submit for Review

Create a pull request with clear description and context.

Use the PR template. Include what changed, why it changed, and how to test. Link to related issues. Add screenshots for UI changes. Request review from at least one team member.

Reference: Engineering Playbook, Section 5

7. Address Feedback

Engage with reviewer comments thoughtfully and promptly.

Respond to all feedback within 24 hours. If you disagree with a suggestion, explain your reasoning. Make requested changes or discuss alternatives. Reviews are collaborative, not adversarial.

Reference: Engineering Playbook, Section 5

8. Monitor After Deploy

Track your changes in production to catch issues early.

After merging, monitor logs and metrics for at least one hour. Set up relevant alerts if needed. If issues arise, address them quickly or revert. Ownership extends beyond the merge button.

Reference: Engineering Playbook, Section 6

Deployment Environments

Development → Staging → Production

  • Development: Active development and testing
  • Staging: Pre-production validation
  • Production: Live user traffic

Every change flows through all three environments. Deploy to development freely for testing. Deploy to staging after tests pass. Deploy to production only with tech lead approval after staging validation.

Reference: Engineering Playbook, Section 6

Code Review Principles

Focus on Clarity

Code should be understandable by someone unfamiliar with the context. Prioritize readability over cleverness.

Assume Positive Intent

Approach reviews collaboratively. Authors did their best. Reviewers want to help improve the code.

Be Specific

Vague feedback like “this could be better” is not helpful. Point to specific lines and suggest concrete improvements.

Ask Questions

When uncertain about an approach, ask why rather than demanding changes. Understanding comes first.

Reference: Engineering Playbook, Section 5

Pre-Submission Checklist

  • All tests pass locally
  • Linter shows no errors
  • Code follows naming conventions
  • New functionality includes tests
  • Documentation updated where relevant
  • No secrets or credentials committed
  • Commit messages are descriptive
  • PR description explains what and why

Reference: Engineering Playbook, Section 5.2

Naming Conventions

JavaScript & TypeScript

  • Variables: camelCase
  • Functions: camelCase
  • Components: PascalCase
  • Constants: SCREAMING_SNAKE_CASE
  • Files: kebab-case

Python

  • Variables: snake_case
  • Functions: snake_case
  • Classes: PascalCase
  • Constants: SCREAMING_SNAKE_CASE
  • Files: kebab-case

Go

  • Variables: camelCase
  • Exported Types: PascalCase
  • Packages: lowercase
  • Files: kebab-case

Reference: Naming Conventions Guide, Section 2

Edit this page

On this page

Git Workflow: React Development

A practical guide for working with the Orcta React template.

Repository: Orctatech-Engineering-Team/orcta-react-template

Branching Model

We follow a simplified Git Flow that prioritizes clarity and reduces merge conflicts. All feature work happens in isolated branches that merge back to development.

Branch Structure

  • main → Production-ready code. Protected branch. Only tech leads can merge.
  • dev → Integration branch. All features merge here first for testing.
  • feature/* → Individual feature branches. Created from dev, merged back to dev.
  • hotfix/* → Emergency fixes. Created from main, merged to both main and dev.

Reference: Engineering Playbook, Section 4.2

Complete Workflow

Step 1: Clone and Setup

Start by creating your project from the React template on GitHub. Go to the template repository, select “Use this template”, create your new repository, then clone it to your machine.

# After creating your repo from the template
# Clone the newly generated repository
git clone git@github.com:Orctatech-Engineering-Team/YOUR-NEW-REPO.git

cd YOUR-NEW-REPO

# Install dependencies
pnpm install

# Verify the setup
pnpm run dev
pnpm test

First Time Setup: Ensure you have Node.js 20+ and pnpm 10+ installed. The template includes pre-configured ESLint, Prettier, and TypeScript settings. Run the linter with npm run lint before committing.

Step 2: Create a Feature Branch

Always create a new branch from the latest dev branch. Never work directly on main or dev.

# Switch to dev and get latest changes
git checkout dev
git pull origin dev

# Create your feature branch
git checkout -b feature/user-authentication

Branch Naming Convention: Use descriptive kebab-case names: feature/user-profile-page, feature/payment-integration, hotfix/login-redirect-bug

Step 3: Develop Your Feature

Build your feature following React best practices and the template’s folder structure.

src/
  components/
    user-profile/
      user-profile.tsx
      user-profile.test.tsx
      user-profile.module.css

Conventions:

  • Component names use PascalCase but their file names are kebab-case: user-profile.tsx
  • Component folders use kebab-case: user-profile/
  • Test files match component names: user-profile.test.tsx
  • CSS modules use kebab-case: user-profile.module.css

Reference: Naming Conventions Guide, Section 2.4

Step 4: Commit Your Changes

Make atomic commits with clear, descriptive messages in imperative mood.

# Stage your changes
git add src/components/user-profile/

# Commit with descriptive message
git commit -m "Add user profile component with avatar display"

Good Commits:

  • Add user authentication form
  • Fix navigation menu overflow on mobile
  • Update API endpoint for user profiles
  • Refactor payment form validation logic

Bad Commits:

  • Fixed stuff
  • Update
  • WIP
  • asdfasdf
  • Added feature and fixed bug and updated docs

Reference: Engineering Playbook, Section 4.1

Step 5: Keep Your Branch Updated

Regularly sync your feature branch with dev to minimize merge conflicts.

# Fetch latest changes from dev
git fetch origin dev

# Rebase your feature branch on top of dev
git rebase origin/dev

# If conflicts occur, resolve them and continue
# Edit conflicted files, then:
git add .
git rebase --continue

Rebase vs Merge: We prefer rebasing feature branches to maintain a linear history. This keeps the commit log clean and makes it easier to understand the evolution of the codebase. Only merge when integrating into dev or main.

Step 6: Run Tests and Linting

Ensure all tests pass and code meets quality standards before pushing.

# Run tests
pnpm test

# Run linter
pnpm run lint

# Fix auto-fixable linting issues
pnpm run lint:fix

# Check TypeScript types
pnpm run type-check

# Build to verify no errors
pnpm run build

Pre-commit Hooks: The template includes Husky pre-commit hooks that automatically run linting and tests. If these checks fail, the commit will be blocked. Fix the issues before committing.

Step 7: Push Your Branch

Push your feature branch to the remote repository.

# Push your branch for the first time
git push -u origin feature/user-authentication

# Subsequent pushes (after first push)
git push

# Force push after rebasing (use with caution)
git push --force-with-lease

Force Pushing: Use --force-with-lease instead of --force when you need to force push after rebasing. This prevents accidentally overwriting someone else’s work if they’ve pushed to your branch.

Step 8: Create a Pull Request

Open a pull request from your feature branch to dev. Use the PR template.

# Via GitHub CLI (if installed)
gh pr create --base dev --title "Add user authentication"

# Or visit GitHub and create PR manually
# The PR template will auto-populate

PR Requirements:

  • Base branch should always be dev (not main)
  • Fill out all sections of the PR template
  • Link to related issues or tickets
  • Add screenshots for UI changes
  • Request review from at least one team member
  • Add appropriate labels: feature, bug fix, refactor, etc.

Reference: Engineering Playbook, Section 4.3

Step 9: Address Review Feedback

Respond to reviewer comments and make requested changes.

# Make changes based on feedback
# Edit files, then commit
git add .
git commit -m "Address review feedback: improve error handling"

# Push changes
git push

# PR will automatically update

Review Etiquette: Respond to all comments, even if just to acknowledge. If you disagree with a suggestion, explain your reasoning respectfully. Mark conversations as resolved once addressed. Reviewers should respond within 48 hours.

Reference: Engineering Playbook, Section 5.3

Step 10: Merge and Clean Up

Once approved, merge your PR and delete the feature branch.

# After PR is approved and CI passes
# Merge via GitHub UI (squash commits)

# Then locally, clean up your branch
git checkout dev
git pull origin dev

# Delete local feature branch
git branch -d feature/user-authentication

# Delete remote branch (usually auto-deleted by GitHub)
git push origin --delete feature/user-authentication

Merge Strategy: We use squash merging to keep the dev and main branches clean. All commits from your feature branch are combined into a single commit. Write a clear merge commit message that summarizes the entire feature.

Reference: Engineering Playbook, Section 4.3

Common Scenarios

Scenario: Working on Multiple Features

You’re working on a user profile feature, but need to start a hotfix for a critical bug.

# Commit your current work
git add .
git commit -m "WIP: user profile layout"

# Switch to main for hotfix
git checkout main
git pull origin main
git checkout -b hotfix/login-redirect

# Fix bug, commit, and create PR
# After hotfix is merged, return to feature
git checkout feature/user-profile

Scenario: Merge Conflict During Rebase

You’re rebasing your feature branch and encounter conflicts.

git rebase origin/dev

# Git shows conflicts in specific files
# Open conflicted files and look for markers:
# <<<<<<< HEAD
# Your changes
# =======
# Their changes
# >>>>>>>

# Edit files to resolve conflicts
# Remove conflict markers
# Keep the correct code

git add .
git rebase --continue

# If you want to abort the rebase
git rebase --abort

Scenario: Accidentally Committed to Wrong Branch

You made commits to dev instead of a feature branch.

# Currently on dev with uncommitted changes
# Create feature branch from current state
git checkout -b feature/my-feature

# Go back to dev and reset it
git checkout dev
git reset --hard origin/dev

# Your changes are now safely in feature branch
git checkout feature/my-feature

Scenario: Need to Update PR After Rebase

You rebased your feature branch and need to update the PR.

# After rebasing
git rebase origin/dev

# Force push with safety check
git push --force-with-lease

# PR will automatically update
# Add a comment explaining the rebase

Quick Reference Commands

# Check current branch and status
git status
git branch

# View commit history
git log --oneline --graph

# Undo last commit (keep changes)
git reset --soft HEAD~1

# Discard local changes
git checkout -- .

# Stash changes temporarily
git stash
git stash pop

# View diff before committing
git diff

# Amend last commit message
git commit --amend

# List all branches
git branch -a

# Clean up deleted remote branches
git fetch --prune
Edit this page

On this page

Pull Request Guidelines

What to include in every pull request. Checklist for authors and guidelines for reviewers.

PR Template Overview

Every pull request at Orcta uses a standard template to ensure consistency, clarity, and thoroughness. The template guides authors through documenting their changes and helps reviewers understand the context quickly.

Standard Template Structure

Summary Brief description of what this PR does in 2-3 sentences. Focus on the what, not the how.

Motivation Why are we making this change? Link to the issue or ticket, or explain the user need if no issue exists.

Changes Made List the key changes in this PR. Helps reviewers understand scope at a glance.

Testing Instructions Step-by-step guide for reviewers to verify the changes work as expected.

Screenshots or Evidence For UI changes: before and after screenshots. For backend: test output or API responses.

Pre-Submission Checklist Items to verify before requesting review. Ensures quality standards are met.

Technical Decisions Document trade-offs, alternative approaches considered, or technical debt introduced.

Related Work Links to dependent PRs, related issues, or follow-up work.

Reviewer Notes Specific areas where you want focused feedback or context that helps reviewers.

Reference: Engineering Playbook, Section 5.2

Author Checklist

Complete all items before requesting review. These checks ensure your PR meets quality standards and is ready for team review.

  • Tests pass locally — Run the full test suite before pushing
  • Linter passes — No ESLint, Flake8, or golangci-lint errors
  • Tests added or updated — New functionality includes corresponding tests
  • Naming conventions followed — Code follows language-specific style guide
  • Documentation updated — README, inline comments, or Notion docs reflect changes
  • No secrets committed — Verify with git diff before pushing
  • Code refactored — Left the codebase better than you found it

Pre-commit Hooks: Many of these checks run automatically via pre-commit hooks. If hooks fail, fix the issues before committing.

Writing Effective PR Descriptions

Good Summary

“Add user authentication with email and password. Implements login form, session management, and protected routes. Users can now sign in and access their profile pages.”

Bad Summary

“Added some auth stuff and fixed a few things. Should work now.”

What makes a good summary? Be specific about what changed. Include the scope and impact. Write for someone who wasn’t involved in the work. Avoid vague terms like “fixed stuff” or “updated things.”

Good Testing Instructions

  1. Start the dev server with npm run dev
  2. Navigate to /login
  3. Enter test@example.com / password123
  4. Verify redirect to /dashboard
  5. Check session persists on page refresh

Bad Testing Instructions

“Just test the login feature. Should be obvious.”

What makes good testing instructions? Step-by-step clarity. Include specific values to test with. Explain what success looks like. Don’t assume reviewers know your feature intimately.

Guidelines for Reviewers

Code review is a collaborative process to improve code quality and share knowledge. Reviews should be thorough but kind, constructive but efficient.

What to Review

  • Correctness of the implementation
  • Code readability and clarity
  • Test coverage and quality
  • Edge cases and error handling
  • Performance implications
  • Security considerations
  • Adherence to conventions

How to Review

  • Respond within 48 hours
  • Start with what works well
  • Ask questions before demanding changes
  • Be specific in your feedback
  • Suggest alternatives, don’t just criticize
  • Focus on substance over style
  • Assume positive intent

Reference: Engineering Playbook, Section 5.3

Feedback Best Practices

Effective Feedback

“This function could be more readable. Consider extracting the validation logic into a separate helper function. That would make it easier to test and reuse.”

Ineffective Feedback

“This is messy. Refactor it.”

What makes feedback effective? Point to specific lines or sections. Explain why something matters. Suggest concrete alternatives. Frame as collaboration, not criticism.

Constructive Question

“I notice we’re not handling the case where the API returns a 429. Is rate limiting handled elsewhere, or should we add retry logic here?”

Unconstructive Demand

“You forgot error handling. Add it.”

When to ask vs tell? Ask questions when you’re uncertain or want to understand the author’s reasoning. Make direct suggestions for clear improvements. Frame as collaboration whenever possible.

Common Review Patterns

Nitpick Pattern

For minor style or preference issues that don’t affect functionality, prefix with “Nit:” to indicate it’s not blocking. Example: “Nit: This variable name could be more descriptive, but not critical.”

Blocking vs Non-Blocking

Be clear about what must be fixed before merge versus what could be done in a follow-up. Use labels like “Blocking:” for critical issues and “Follow-up:” for nice-to-haves.

Learning Opportunity

Use reviews to teach and learn. Share relevant documentation, patterns, or examples. Explain the reasoning behind suggestions so authors understand the principles, not just the fixes.

Response Timeline

Timely reviews keep development moving and prevent context switching.

For Reviewers

  • First response within 48 hours
  • Subsequent reviews within 24 hours
  • If you can’t review, say so early
  • Unblock authors by reviewing incrementally
  • Prioritize reviews during maker hours

For Authors

  • Address feedback within 24 hours
  • Respond to all comments, even simple “Done”
  • Explain if you disagree with feedback
  • Mark conversations as resolved when fixed
  • Request re-review when ready

When Reviews Stall: If feedback sits unaddressed for 3+ days, or a PR waits for review beyond 48 hours, escalate to a tech lead. Sometimes a quick sync call can resolve blockers faster than async comments.

Approval and Merge

Approval Requirements

At least one approval required from a team member. For significant architectural changes, get approval from a tech lead. All CI checks must pass before merging.

Merge Strategy

We use squash merging to keep git history clean. All commits from your feature branch combine into one. Write a clear merge commit message that summarizes the entire feature.

After Merge

Monitor your changes in production for at least one hour. Delete your feature branch. Close related issues. Update documentation if needed.

Reference: Engineering Playbook, Section 4.3

Philosophy

Code review is about improving code, not judging people. We assume positive intent, engage collaboratively, and recognize that everyone is learning. Great reviews make the code better and help everyone grow as engineers.

Edit this page

On this page

Playbook in Practice

Real stories from Orcta Engineering. Learn from what went well and what didn’t.

Story One: The Minimum Lovable Product That Launched in Two Weeks

Context

Product requested a comprehensive user dashboard with fifteen distinct features. The initial timeline was set for one month of development work. The team needed to deliver value quickly while managing scope.

What We Did

  • Asked the fundamental question: what is the one thing users need most?
  • Shipped just the critical metrics view in the first week
  • Gathered real user feedback on actual usage patterns
  • Added three more features in week two based on observed behavior
  • Delivered a complete, usable product on schedule

Outcome

  • Users engaged heavily with the initial release
  • Data showed 60% of originally planned features were unnecessary
  • Saved three weeks of engineering time
  • Delivered higher-quality features informed by real usage

Key Lesson

Start with why and validate with real users before building everything. Assumptions about user needs are often wrong until tested.

Engineering Philosophy: Minimum Lovable Product — Build the smallest version users can love


Story Two: The Code Review That Prevented a Critical Bug

Context

A pull request introduced payment processing functionality. All tests passed. The implementation appeared sound on first inspection. The PR was ready for approval.

What Happened

During review, an engineer noticed missing error handling for network failures. They asked a simple question: what happens if the payment API times out?

The author realized users would be charged but orders wouldn’t be recorded in our system. A critical data integrity issue that would have caused significant problems in production.

What We Did

  • Added retry logic with exponential backoff for transient failures
  • Implemented transaction rollback on payment confirmation failure
  • Created integration tests specifically for timeout scenarios
  • Documented the edge case for future reference

Outcome

  • Caught a critical bug before it reached production
  • Prevented potential revenue loss and customer trust issues
  • Improved the payment system’s overall reliability
  • Created reusable patterns for similar integrations

Key Lesson

Code reviews aren’t about finding typos. They’re about protecting users and the business by thinking through edge cases together.

Engineering Playbook, Section 5: Code reviews are sacred — feedback must be kind, clear, and focused on improvement


Story Three: When We Ignored Refactor as You Go

Context

While building a new API endpoint, an engineer noticed duplicated authentication logic across five different controllers. The code worked, but the duplication was obvious.

What We Did Wrong

The team decided to ship quickly with a note: we’ll refactor later when we have time. The authentication code was copied one more time. Development continued.

Three months passed. A security vulnerability was discovered in the authentication logic. The fix needed to be applied in six different places across the codebase.

The Incident

Two instances of the duplicated code were missed during the fix. Production experienced a two-hour outage when those endpoints were exploited. Customer data was not compromised, but trust was shaken.

What We Should Have Done

Invested thirty minutes to extract the authentication logic into a reusable middleware component. Fixed it once, used it everywhere. The vulnerability would have required one change in one place.

Outcome

  • Two-hour production outage affecting all users
  • Emergency incident response requiring all-hands effort
  • Blameless postmortem conducted within 48 hours
  • New team agreement: refactor duplicated code immediately

Key Lesson

Later usually means never. Technical debt compounds. What takes thirty minutes today costs ten times that in three months, plus the cost of the incident.

Engineering Philosophy, Section 3: Refactor as you go — Don’t postpone improvements


Story Four: The Documentation That Unblocked Three Teams

Context

A new internal API for user permissions was built and deployed. No formal documentation was written. Information was shared through Slack messages and verbal explanations.

What Happened

Over the next month, three different teams needed to integrate with the permissions API. Each team reached out with similar questions about authentication, endpoint structure, and error handling.

The original author spent over six hours answering repetitive questions in Slack DMs and ad-hoc meetings. Integration took each team longer than necessary due to missing context.

What We Did to Fix It

Invested fifteen minutes writing a clear README with essential information:

  • What the API does and why it exists
  • How to authenticate and handle tokens
  • Three common use cases with code examples
  • Known limitations and error scenarios
  • Inline code comments for complex logic

Outcome

  • Repetitive questions stopped immediately
  • Teams integrated independently without blocking the author
  • README was referenced over forty times in two months
  • Onboarding new engineers to the system became trivial

Key Lesson

Fifteen minutes of documentation saves ten hours of interruptions. Documentation is generosity to your teammates and your future self.

Engineering Philosophy, Section 2: Document to scale — Documentation is an act of generosity


Story Five: The Postmortem That Made Us Better

Context

Production database ran out of connections during peak traffic. The site went down for forty-five minutes. Users couldn’t access the application. Support tickets flooded in.

What We Did

Held a blameless postmortem within forty-eight hours of resolution. The team asked five whys to understand root causes:

Five Whys Analysis:

Why did the database run out of connections? Connection pool wasn’t sized correctly for peak load.

Why wasn’t it sized correctly? We didn’t load test before launch.

Why didn’t we load test? No documented process or checklist for pre-launch testing.

Why no process? Never formalized what everyone assumed was known.

Why wasn’t it formalized? Tribal knowledge instead of written procedures.

Actions Taken

  • Created comprehensive pre-launch checklist including load testing
  • Set up database connection monitoring with alerts
  • Documented runbook for connection pool issues
  • Made pre-launch checklist mandatory via PR template
  • Scheduled quarterly review of operational procedures

Outcome

  • No similar incidents in the following six months
  • Pre-launch checklist caught two other potential issues
  • Team felt safe discussing mistakes without blame
  • Culture of learning from failure was strengthened

Key Lesson

Systems fail. Humans make mistakes. How we respond to failure defines our culture. Blameless analysis and systematic improvement matter more than perfect execution.

Engineering Philosophy, Section 3: Fail fast, learn faster — Mistakes are okay, cover them in postmortems


Story Six: When Ownership Meant Heroism

Context

An engineer deployed a new feature Friday evening. At eleven PM, they received a page about a bug in production. The feature had an edge case that wasn’t caught in testing.

What Happened

The engineer felt personally responsible. They believed ownership meant solving it alone. They stayed up until two AM debugging and deploying a fix. The issue was resolved but the engineer was exhausted.

The following week, they felt burned out. Work-life balance suffered. The incident response, while successful, wasn’t sustainable.

What We Should Have Done

  • The on-call engineer should have handled the initial response
  • If the feature author wanted to help, pair with on-call instead of solo work
  • For critical issues, wake the tech lead for support and guidance
  • Follow established incident response procedures
  • No one should feel obligated to sacrifice sleep

What We Changed

  • Clarified that ownership doesn’t mean martyrdom
  • Set clear on-call expectations and rotation schedules
  • Added retro question: did anyone feel unsupported this week?
  • Emphasized collaborative incident response in documentation
  • Leadership modeled asking for help publicly

Outcome

  • Better work-life balance across the team
  • More effective collaborative incident response
  • Reduced individual stress and burnout
  • No one felt guilty for asking for help

Key Lesson

Ownership means responsibility, not isolation. We own problems collectively, not individually. Sustainable engineering requires sustainable practices.

Engineering Philosophy, Section 2: Ownership mentality — But we own problems collectively


How to Use These Stories

In Code Reviews

Reference relevant stories when providing feedback. “This reminds me of Story Two—can we add error handling here?”

In Standups

Connect current work to past lessons. “Feels like Story Three—should we refactor now before it spreads?”

In Retrospectives

Use stories to frame discussions. “This situation is similar to Story Four—let’s document this so it doesn’t happen again.”

In Onboarding

Share stories with new engineers to explain why the playbook exists and how principles apply in practice.

Add Your Own Stories

Saw something that reinforces or challenges our principles? Share it. These stories are our institutional knowledge.

  1. Write it up using the format: Context, What Happened, Outcome, Key Lesson
  2. Post in the engineering Slack channel for discussion
  3. Submit a pull request to add it to this document
  4. Share the lesson in the next team meeting
Edit this page

On this page

Onboarding Checklist

A lightweight 90-day plan for new engineers to ramp up, build context, and take ownership.

Days 1-7: Foundations

Access Setup

  1. GitHub: Join the Orctatech-Engineering-Team organization
  2. Tailscale: Install Tailscale and join the Orcta-tech network
    # Install Tailscale
    curl -fsSL https://tailscale.com/install.sh | sh
    sudo tailscale up
    Ask an admin to approve your device in the Tailscale admin console.
  3. VPS Access: Once approved on Tailscale, SSH to the VPS:
    ssh bernard@100.125.3.44
  4. Orcta Runtime: Access the dashboard at https://runtime.orctatech.com/login
    • Ask an admin for an API token
    • Or use GitHub OAuth if configured

Documentation

Meet Your Team

Days 8-30: Contribution

  • Ship at least one scoped change with a full PR.
  • Pair on a medium-sized task to learn team patterns.
  • Document one unclear process you encounter.

Days 31-60: Ownership

  • Own a feature or component end-to-end.
  • Improve tests or reliability in an existing area.
  • Present a short demo or write-up of what you shipped.

Days 61-90: Deepening Impact

  • Identify a meaningful system improvement and propose a plan.
  • Lead a PR review or small release.
  • Align with your manager on growth goals for the next quarter.
Edit this page

Engineering Philosophy

Our philosophy anchors how we design, build, and ship.

Principles

  • Principles over tools: pick the best tool for the job, but protect the fundamentals.
  • Progressive abstraction: start simple, abstract only when patterns repeat.
  • Craftsmanship: readable, reliable systems beat clever shortcuts.
  • Human experience: performance and reliability must serve real users.
Edit this page

Engineering Playbook

A shared set of practices, rituals, and standards that keep delivery consistent.

Core Practices

  • Design before implementation for anything beyond a small change.
  • Write tests for business logic and user-facing behavior.
  • Review for clarity, correctness, and maintainability.
  • Monitor production after releases and document outcomes.

Rituals

  • Weekly planning and scope alignment.
  • Code reviews within 48 hours.
  • Post-release checks on critical systems.
Edit this page

Naming Conventions

Keep naming consistent so the codebase stays readable across teams.

JavaScript & TypeScript

  • Variables: camelCase
  • Functions: camelCase
  • Components: PascalCase
  • Constants: SCREAMING_SNAKE_CASE
  • Files: kebab-case

Python

  • Variables: snake_case
  • Functions: snake_case
  • Classes: PascalCase
  • Constants: SCREAMING_SNAKE_CASE
  • Files: kebab-case

Go

  • Variables: camelCase
  • Exported Types: PascalCase
  • Packages: lowercase
  • Files: kebab-case
Edit this page

Pre-commit Hooks Setup

Use the repo’s default tooling to run checks locally before you open a PR.

Standard Setup

  1. Read the project README for stack-specific steps.
  2. Install dependencies (example: pnpm install or npm install).
  3. If the repo uses pre-commit, run pre-commit install.
  4. Run the full check once: pre-commit run --all-files.

Expectations

  • Fix lint and formatting issues before committing.
  • Keep the hook configuration in the repo, not in local overrides.
Edit this page

On this page

Infrastructure

Operational documentation for Orcta production infrastructure and deployment workflows.

Core Docs

Security

Services

Service URL Port Purpose
Orcta Runtime runtime.orctatech.com 9010 Deployment automation
LogDeck logdeck.orctatech.com 8123 Log monitoring
Vault vault.orctatech.com 8200 Secrets management
PSS pss.orctatech.com 3000 PSS frontend
AMES ames.orctatech.com 3002 AMES frontend
Waybills waybills.orctatech.com 3000 Waybills frontend

Monitoring

Lookout (Server Monitoring)

  • Status: Running as systemd service
  • Config: /etc/lookout/config.yaml
  • Monitors: CPU, memory, disk, Docker containers, TCP services
  • Checks: Orcta Runtime health, Postgres, Redis, Valkey, PgBouncer

LogDeck (Log Monitoring)

  • URL: https://logdeck.orctatech.com
  • Features: Real-time log streaming, alerting, container management
  • Integration: Sends alerts to Orcta Runtime via webhook

Orcta Runtime (Deployment Automation)

  • URL: https://runtime.orctatech.com
  • Features: Multi-app deploys, GitHub webhooks, health checks, rollback
  • Dashboard: https://runtime.orctatech.com/login

Quick Reference

# SSH to VPS (via Tailscale)
ssh bernard@100.125.3.44

# Check all containers
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'

# Check Orcta Runtime health
curl -s http://localhost:9010/health

# Check Lookout status
journalctl -u lookout -f

# Check Caddy logs
journalctl -u caddy --no-pager -n 50

# Vault operations
vault status
vault login -method=userpass username=cto password='your-password'
vault kv get secret/orcta/pss/database
Edit this page

On this page

CI/CD Pipeline

This runbook explains how Orcta deploys services from GitHub to the VPS using GitHub Actions, Docker, and a shared PostgreSQL cluster.

High-Level Architecture

  • Codebase: GitHub (dev and main branches).
  • CI/CD: GitHub Actions (SSH deploy to VPS) or Orcta Runtime (webhook-triggered).
  • Runtime: Ubuntu VPS running Docker + Docker Compose.
  • Deployment Automation: Orcta Runtime (runtime.orctatech.com) manages multi-app deployments.
  • Monitoring: Lookout (server metrics) + LogDeck (log monitoring).
  • Services:
    • App containers per repo.
    • Shared PostgreSQL cluster (Dockerized) with per-app DBs/roles.
    • Caddy reverse proxy for routing and TLS.

Deployment Flow

  1. Push to main or dev.
  2. GitHub fires webhook to runtime.orctatech.com/webhook.
  3. Orcta Runtime pulls the new image, runs pre-deploy health checks.
  4. Orcta starts the container with docker compose up -d.
  5. Health check polling verifies the app is healthy.
  6. On failure, automatic rollback to last known good version.

Option 2: Direct SSH (Legacy)

  1. Push to main or dev.
  2. GitHub Actions connects to the VPS via SSH.
  3. The repo is cloned or pulled into /srv/apps/<APP>.
  4. Docker Compose rebuilds and restarts the service.
  5. Caddy reloads to pick up new routes.

Architecture Diagram

             ┌───────────────────────┐
             │       GitHub Repo     │
             │  (main & dev branches)│
             └───────────┬───────────┘
                         │
                    Push triggers
                         │
                         ▼
             ┌───────────────────────┐
             │    GitHub Actions     │
             │  (CI/CD Workflows)    │
             └───────────┬───────────┘
                         │
                   SSH Deploys to
                         │
                         ▼
    ┌─────────────────────────────────────────┐
    │                 VPS                     │
    │   (Ubuntu + Docker + Docker Compose)    │
    │                                         │
    │  ┌───────────────────────────────────┐  │
    │  │      Shared Docker Network        │  │
    │  │         `orcta-net`               │  │
    │  │                                   │  │
    │  │  ┌─────────────┐   ┌────────────┐ │  │
    │  │  │   app1      │   │   app2     │ │  │
    │  │  │ dockerized  │   │ dockerized │ │  │
    │  │  │ service     │   │ service    │ │  │
    │  │  └─────┬───────┘   └──────┬─────┘ │  │
    │  │        │                  │       │  │
    │  │        ▼                  ▼       │  │
    │  │   postgres://app1_db   postgres://app2_db
    │  │                                   │  │
    │  │        ┌───────────────────┐      │  │
    │  │        │   PostgreSQL DB   │      │  │
    │  │        │   dockerized      │      │  │
    │  │        │   per-app roles   │      │  │
    │  │        └───────────────────┘      │  │
    │  └───────────────────────────────────┘  │
    │                                         │
    │ Backups → /srv/orcta/postgres/backups/  │
    └─────────────────────────────────────────┘

VPS Directory Layout

/srv/apps
  app1/
    docker-compose.yml
    Caddyfile-snippet
  app2/
    docker-compose.yml
    Caddyfile-snippet

/srv/orcta-postgres
  docker-compose.yml
  init/
    01-init.sql
  backups/
  backup.sh

GitHub Actions (Example)

.github/workflows/deploy.yml

name: CI/CD Pipeline (Direct VPS Build)

on:
  push:
    branches: [dev, main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Deploy on VPS
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          script: |
            set -e
            APP=orcta-app
            mkdir -p /srv/apps/$APP
            cd /srv/apps/$APP

            if [ ! -d ".git" ]; then
              git clone git@github.com:Orctatech-Engineering-Team/$APP.git .
            else
              git pull origin main
            fi

            docker compose up --build -d

PostgreSQL Setup

/srv/orcta-postgres/docker-compose.yml

version: "3.9"
services:
  postgres:
    image: postgres:16
    container_name: orcta_postgres
    restart: always
    environment:
      POSTGRES_PASSWORD: <set-in-env>
    volumes:
      - ./data:/var/lib/postgresql/data
      - ./init:/docker-entrypoint-initdb.d
    networks:
      - orcta_net

  pgbouncer:
    image: edoburu/pgbouncer
    container_name: orcta_pgbouncer
    restart: always
    ports:
      - "6432:6432"
    environment:
      DB_USER: pgbouncer
      DB_PASSWORD: <set-in-env>
      DB_HOST: postgres
      DB_PORT: 5432
      POOL_MODE: transaction
      MAX_CLIENT_CONN: 200
      DEFAULT_POOL_SIZE: 20
    depends_on:
      - postgres
    networks:
      - orcta_net

networks:
  orcta_net:
    external: true

Example init script (/srv/orcta-postgres/init/01-init.sql):

CREATE USER app1_user WITH PASSWORD '<password>';
CREATE DATABASE app1_db OWNER app1_user;
REVOKE ALL ON DATABASE app1_db FROM PUBLIC;

Caddy Reverse Proxy

Main /etc/caddy/Caddyfile:

{
    email admin@orctatech.com
}

import /srv/apps/*/Caddyfile-snippet

Example snippet:

orcta.example.com {
    reverse_proxy localhost:9999
}

Backups

/srv/orcta-postgres/backup.sh:

#!/bin/bash
TIMESTAMP=$(date +%F_%H-%M-%S)
docker exec -t orcta-postgres pg_dumpall -U postgres > /srv/orcta-postgres/backups/db_$TIMESTAMP.sql

Cron (2 AM daily):

0 2 * * * /srv/orcta-postgres/backup.sh

Monitoring & Troubleshooting

  • App logs: docker logs -f <container>
  • Postgres logs: docker logs -f orcta-postgres
  • Caddy logs: journalctl -u caddy --no-pager -n 100
  • Orcta Runtime dashboard: https://runtime.orctatech.com/login
  • Lookout status: journalctl -u lookout -f
  • LogDeck: https://logdeck.orctatech.com

Orcta Runtime Setup

Orcta Runtime is the deployment automation platform that manages multi-app Docker Compose deploys.

Initial Setup

# On the VPS
git clone git@github.com:Orctatech-Engineering-Team/orcta-runtime.git ~/orcta-runtime
cd ~/orcta-runtime

# Create .env
cp .env.example .env
# Edit .env with:
#   AUTH_TOKEN=<your-admin-token>
#   WEBHOOK_SECRET=<your-github-webhook-secret>

# Build and start
docker compose build
docker compose up -d

Register an App

# Create an app entry
curl -X POST https://runtime.orctatech.com/api/apps \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-app",
    "compose_dir": "/srv/apps/my-app",
    "health_url": "http://localhost:3000/health",
    "timeout_sec": 60
  }'

Configure GitHub Webhook

In your GitHub repo → Settings → Webhooks → Add webhook:

  • Payload URL: https://runtime.orctatech.com/webhook
  • Content type: application/json
  • Secret: Same as WEBHOOK_SECRET in .env
  • Events: Just the push event

Install Monitoring (Lookout)

# Run the bootstrap action via API
curl -X POST https://runtime.orctatech.com/api/execute/bootstrap-monitoring \
  -H "Authorization: Bearer <operator-token>"

Or install manually:

curl -fsSL https://raw.githubusercontent.com/AmoabaKelvin/lookout/main/install.sh | sudo sh
Edit this page

On this page

VPS Setup & Hardening

This guide summarizes the initial setup and security hardening steps for a new Ubuntu VPS. The process is automated in an idempotent, interactive script.

Current VPS

  • Provider: Contabo VPS
  • Hostname: vmi2813472
  • Public IP: 158.220.100.30
  • OS: Ubuntu 24.04 (Noble)
  • Tailscale IP: 100.125.3.44

Script Overview

The setup script performs the following:

  • Creates users and adds them to sudo.
  • Installs and configures SSH keys.
  • Disables password auth and root login.
  • Optionally changes the SSH port.
  • Installs and enables UFW, Fail2Ban, and unattended upgrades.
  • Logs output to /var/log/vps-setup.log.

Usage

sudo bash vps-setup.sh

Key Steps (Manual Reference)

System Updates

sudo apt update && sudo apt upgrade -y

SSH Hardening

  • PasswordAuthentication no
  • PermitRootLogin no
  • PubkeyAuthentication yes

Config file: /etc/ssh/sshd_config

Tailscale VPN

Tailscale provides secure access to the VPS without exposing SSH to the public internet.

# Install Tailscale
curl -fsSL https://tailscale.com/install.sh | sh

# Start and enable
sudo systemctl enable --now tailscaled

# Authenticate (follow the URL)
sudo tailscale up

# Verify
tailscale status
tailscale ip -4

Current Tailscale network:

Host Tailscale IP User Status
vmi2813472 (VPS) 100.125.3.44 tagged-devices Active
omarchy (laptop) 100.91.177.47 Adjanour@ Active
desktop-uh96ibf 100.126.53.9 tedlee.orctech@ Offline

SSH via Tailscale:

# From any Tailscale-connected machine
ssh bernard@100.125.3.44

# Or using hostname
ssh bernard@vmi2813472

Firewall

sudo apt install ufw -y
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable

Fail2Ban

sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Automatic Updates

sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure -f noninteractive unattended-upgrades

Logging

All script output is written to /var/log/vps-setup.log with restricted permissions.

Troubleshooting

  • If SSH access breaks after changing ports, revert the port via console access.
  • Confirm SSH service is running: sudo systemctl status ssh.
  • Check Tailscale status: tailscale status or systemctl status tailscaled.
Edit this page

On this page

SSH & Tailscale Setup

Architecture

Laptop ──► Tailscale ──► 100.125.3.44:22 ──► sshd
              │                                    │
              │                           iptables: accept from
              │                           100.109.232.127 only
              │
        100.109.232.127 (laptop)

SSH Socket Activation

Ubuntu 24.04 manages SSH via systemd socket activation. The ssh.socket unit creates the listening socket and passes it to sshd on connection. This means ListenAddress in sshd_config is ignored — the socket unit controls the bind.

Override file

Path: /etc/systemd/system/ssh.socket.d/override.conf

[Socket]
ListenStream=
ListenStream=100.125.3.44:22
ListenStream=127.0.0.1:22
FreeBind=true
  • ListenStream= (empty) clears the default 0.0.0.0:22 and [::]:22
  • 100.125.3.44:22 is the Tailscale interface IP
  • 127.0.0.1:22 allows localhost access (e.g., from the hosting console)
  • FreeBind=true allows the socket to start even if the Tailscale IP isn’t up yet

Apply

sudo mkdir -p /etc/systemd/system/ssh.socket.d
sudo cp override.conf /etc/systemd/system/ssh.socket.d/
sudo systemctl daemon-reload
sudo systemctl restart ssh.socket

Firewall Rules

Two iptables rules control SSH access:

# Allow only laptop Tailscale IP
iptables -A INPUT -p tcp --dport 22 -s 100.109.232.127 -j ACCEPT
# Drop everything else on port 22
iptables -A INPUT -p tcp --dport 22 -j DROP
# Persist across reboots
iptables-save > /etc/iptables/rules.v4

The iptables-persistent package loads /etc/iptables/rules.v4 on boot.

Defense in Depth

Even though the socket unit already limits the listener to Tailscale/localhost, the iptables rules add a second layer. If Tailscale ever went down and the FreeBind caused sshd to bind elsewhere, the firewall still blocks non-laptop IPs.

Tailscale Migration

Personal → Org

The VPS was on a personal tailnet. Migration steps:

  1. Remove the machine from the personal tailnet via admin console
  2. On the VPS: systemctl stop tailscaled && rm -f /var/lib/tailscale/tailscaled.state
  3. Start tailscaled and authenticate with org auth key:
    systemctl start tailscaled
    tailscale up --auth-key=tskey-auth-<key>

Connection flow

Clients on the org tailnet connect directly (wireguard) to the VPS. No public IP is involved. The Tailscale IP 100.125.3.44 is stable but not guaranteed to never change — if it does, update the ssh socket override.

Local SSH Config

~/.ssh/config on the laptop:

Host orcta_vps
    HostName 100.125.3.44
    User bernard
    IdentityFile ~/.ssh/orcta_id

Without this change, connections still went to 158.220.100.30 (public IP), which now refuses SSH.

Edit this page

On this page

HashiCorp Vault Setup & Configuration

This guide covers the setup, configuration, and usage of HashiCorp Vault for secrets management at Orcta Technologies.

Overview

HashiCorp Vault is our centralized secrets management solution. It provides:

  • Centralized secret storage - All credentials in one secure location
  • Dynamic credentials - Auto-rotating database passwords
  • Audit logging - Complete trail of who accessed what
  • Least privilege access - Role-based policies for team members
  • Encryption as a service - Transit engine for data encryption

Current Setup

  • Vault Version: v2.0.3
  • Storage Backend: File (/opt/vault/data)
  • Listener: localhost:8200 (proxied via Caddy)
  • UI: Available at https://vault.orctatech.com/ui
  • Service: vault.service (systemd, starts on boot)

Access Points

Direct Access (via Tailscale)

ssh orcta_vps
export VAULT_SKIP_VERIFY=true
export VAULT_ADDR='https://127.0.0.1:8200'
vault login -method=userpass username=cto password='your-password'

Web UI Access

https://vault.orctatech.com/ui

Note: Requires DNS record vault.orctatech.com158.220.100.30

Team Access

Role Username Access Level Use Case
CTO cto Full access Infrastructure, secret rotation
DevOps devops Read all, write infra Deployment, monitoring
Developer developer1 Read-only dev/services Local development
CI/CD cicd Write CI, read services GitHub Actions, pipelines

Secret Structure

secret/
├── orcta/
│   ├── pss/                    # PSS Backend
│   │   ├── database            # PostgreSQL credentials
│   │   ├── valkey              # Valkey/Redis credentials
│   │   ├── jwt                 # JWT secrets
│   │   ├── uploadthing         # File upload service
│   │   ├── email               # Email service (Resend)
│   │   ├── auth                # Better Auth secrets
│   │   └── app                 # Application config
│   ├── stack/                  # Orcta Stack
│   ├── mentorship/             # Mentorship Platform
│   ├── docs-mcp/               # Documentation MCP
│   ├── dev/                    # Development secrets
│   ├── prod/                   # Production secrets
│   ├── infra/                  # Infrastructure secrets
│   └── admin/                  # Admin credentials

Common Operations

Login

export VAULT_SKIP_VERIFY=true
export VAULT_ADDR='https://127.0.0.1:8200'
vault login -method=userpass username=YOUR_USERNAME password=YOUR_PASSWORD

Read a Secret

vault kv get secret/orcta/pss/database
vault kv get -field=password secret/orcta/pss/database

Write a Secret

vault kv put secret/orcta/myapp/config \
  api_key="abc123" \
  db_password="secret456"

List Secrets

vault kv list secret/orcta/
vault kv list secret/orcta/pss/

Delete a Secret

vault kv delete secret/orcta/pss/old-secret

PSS Backend Integration

The PSS backend was our pilot project for Vault integration.

Fetch Script

/usr/local/bin/fetch-pss-secrets

This script:

  1. Authenticates to Vault
  2. Fetches all PSS secrets
  3. Generates .env file at /srv/apps/ORCTA-BACKEND-PSS/.env

Usage

# Manual fetch
/usr/local/bin/fetch-pss-secrets

# Or integrate into deployment
./fetch-pss-secrets && docker compose up -d

Caddy Proxy Configuration

Vault is proxied through Caddy for HTTPS access:

vault.orctatech.com {
    reverse_proxy localhost:8200 {
        transport http {
            tls_insecure_skip_verify
        }
    }
    encode zstd gzip
    log {
        output file /var/log/caddy/vault.access.log
        format json
    }
    import security
    
    header {
        X-Frame-Options "SAMEORIGIN"
    }
}

Unsealing Vault

Vault seals on restart and requires 3 of 5 unseal keys:

vault operator unseal <key1>
vault operator unseal <key2>
vault operator unseal <key3>

Unseal Keys (stored securely offline):

1. 7GUabmWbJNrw3IHRiBCugkCsaghxAGIrf7Cq0Kx27d+N
2. u053Y77SME9N5jXdlOosmuAnXHueO8nPC0J9DdZIhdIH
3. rsTB0S4oDjkHjB4UU+YZdjcS/EslKKNzByXTKcDNgoYV
4. kRrQcZ5J01jiSExyOO7GWQQkaTvX+Z0pWMC9daifBbje
5. oyQqHhJ1FfHXcIcf3dJ+3pEudqF7Rt0tUwjW8ZzJxdaq

Audit Logging

All Vault operations are logged to /var/log/vault/audit.log.

View Logs

sudo tail -f /var/log/vault/audit.log

Search for User

sudo grep "username" /var/log/vault/audit.log

systemd Service

Vault runs as a systemd service:

# Check status
sudo systemctl status vault

# Restart
sudo systemctl restart vault

# View logs
sudo journalctl -u vault -f

Security Best Practices

Do’s

✅ Use short-lived tokens - Login again when token expires
✅ Store tokens securely - Never commit to git
✅ Use least privilege - Only access what you need
✅ Rotate secrets regularly - Use Vault’s rotation features
✅ Audit access - Check /var/log/vault/audit.log

Don’ts

❌ Never hardcode secrets - Always use Vault
❌ Never share tokens - Each user gets their own
❌ Never store tokens in git - Use .gitignore
❌ Never use root token - Use your personal token
❌ Never disable audit logging - Compliance requirement

Troubleshooting

“Permission Denied” Error

# Check your policies
vault token lookup

# Check what you can access
vault kv list secret/orcta/

“Vault is Sealed” Error

# Unseal Vault (need 3 of 5 keys)
vault operator unseal <key1>
vault operator unseal <key2>
vault operator unseal <key3>

“Token Expired” Error

# Re-login
vault login -method=userpass username=YOUR_USERNAME password=YOUR_PASSWORD

Check Audit Logs

# View recent audit events
sudo tail -f /var/log/vault/audit.log

# Search for specific user
sudo grep "username" /var/log/vault/audit.log

Environment Variables

Set these in your shell profile (~/.bashrc or ~/.zshrc):

# Vault
export VAULT_SKIP_VERIFY=true
export VAULT_ADDR='https://127.0.0.1:8200'

# Or for remote access via Tailscale
export VAULT_ADDR='https://100.125.3.44:8200'

API Usage

REST API Example

# Login
curl -s -k -X POST https://127.0.0.1:8200/v1/auth/userpass/login/cto \
  -d '{"password":"your-password"}'

# Read secret
curl -s -k -H "X-Vault-Token: YOUR_TOKEN" \
  https://127.0.0.1:8200/v1/secret/data/orcta/pss/database

# Write secret
curl -s -k -X POST -H "X-Vault-Token: YOUR_TOKEN" \
  -d '{"data":{"key":"value"}}' \
  https://127.0.0.1:8200/v1/secret/data/orcta/myapp/config

JavaScript/Node.js

const axios = require('axios');

const VAULT_ADDR = 'https://127.0.0.1:8200';
const VAULT_TOKEN = 'your-token';

async function getSecret(path) {
  const response = await axios.get(
    `${VAULT_ADDR}/v1/secret/data/${path}`,
    { 
      headers: { 'X-Vault-Token': VAULT_TOKEN },
      httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false })
    }
  );
  return response.data.data.data;
}

// Usage
const dbSecret = await getSecret('orcta/pss/database');
console.log(dbSecret.password);

Backup & Recovery

Backup

# Backup Vault data
sudo tar -czf /backup/vault-backup-$(date +%Y%m%d).tar.gz /opt/vault/data

Recovery

# Stop Vault
sudo systemctl stop vault

# Restore data
sudo tar -xzf /backup/vault-backup-YYYYMMDD.tar.gz -C /

# Start Vault
sudo systemctl start vault

# Unseal
vault operator unseal <key1>
vault operator unseal <key2>
vault operator unseal <key3>

Next Steps

Support

Edit this page

On this page

Infrastructure Diagrams

Reference visuals for how our infrastructure and deployment flow fit together.

Deployment Flow (Orcta Runtime)

             ┌───────────────────────┐
             │       GitHub Repo     │
             │  (main & dev branches)│
             └───────────┬───────────┘
                         │
                    Push triggers webhook
                         │
                         ▼
             ┌───────────────────────┐
             │    Orcta Runtime      │
             │  runtime.orctatech.com│
             └───────────┬───────────┘
                         │
                   docker compose
                         │
                         ▼
     ┌─────────────────────────────────────────┐
     │                 VPS                     │
     │   (Ubuntu + Docker + Docker Compose)    │
     │                                         │
     │  ┌───────────────────────────────────┐  │
     │  │      Shared Docker Network        │  │
     │  │         `orcta-net`               │  │
     │  │                                   │  │
     │  │  ┌─────────────┐   ┌────────────┐ │  │
     │  │  │   app1      │   │   app2     │ │  │
     │  │  │ dockerized  │   │ dockerized │ │  │
     │  │  │ service     │   │ service    │ │  │
     │  │  └─────┬───────┘   └──────┬─────┘ │  │
     │  │        │                  │       │  │
     │  │        ▼                  ▼       │  │
     │  │   postgres://app1_db   postgres://app2_db
     │  │                                   │  │
     │  │        ┌───────────────────┐      │  │
     │  │        │   PostgreSQL DB   │      │  │
     │  │        │   dockerized      │      │  │
     │  │        │   per-app roles   │      │  │
     │  │        └───────────────────┘      │  │
     │  └───────────────────────────────────┘  │
     │                                         │
     │ Backups → /srv/orcta/postgres/backups/  │
     └─────────────────────────────────────────┘

Caddy Reverse Proxy

Internet
   │
   ▼
Caddy (TLS + routing)
   │
   ├── runtime.orctatech.com → localhost:9010 (Orcta Runtime)
   ├── logdeck.orctatech.com → localhost:8123 (LogDeck)
   ├── pss.orctatech.com → localhost:3000
   ├── ames.orctatech.com → localhost:3002
   ├── api.pss.orctatech.com → localhost:9999
   └── ... (15+ sites)

Monitoring Stack

┌─────────────────────────────────────────────────────┐
│                   Monitoring                        │
│                                                     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐ │
│  │   Lookout   │  │   LogDeck   │  │   Orcta     │ │
│  │  (systemd)  │  │  (docker)   │  │  Runtime    │ │
│  │             │  │             │  │  (docker)   │ │
│  │ • CPU/RAM   │  │ • Logs      │  │ • Deploys   │ │
│  │ • Disk      │  │ • Alerts    │  │ • Rollback  │ │
│  │ • Docker    │  │ • History   │  │ • Hooks     │ │
│  │ • TCP checks│  │             │  │             │ │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘ │
│         │                │                │         │
│         │    webhook     │                │         │
│         └───────────────►└────────────────┘         │
│                                                     │
└─────────────────────────────────────────────────────┘

Database Backup Flow

Postgres container
   │
   │ pg_dumpall
   ▼
/srv/orcta-postgres/backups/db_<timestamp>.sql
   │
   ▼
Offsite or cold storage (manual or scheduled)
Edit this page

On this page

Security Audit — Initial State

Summary

On 2026-05-25, a ss -tlnp audit revealed that 11 internal services were bound to 0.0.0.0 (all network interfaces), making them reachable from the public internet. Only SSH (22), HTTP (80), and HTTPS (443) should be public.

Services Exposed to 0.0.0.0

Port Service Process Risk
6379 Redis / Valkey docker-proxy (orctadocsmcp-valkey-1) Critical — no auth, database cache
5433 PostgreSQL docker-proxy (orctadocsmcp-postgres-1) Critical — full database access
6432 pgBouncer docker-proxy (orcta_pgbouncer) Critical — database connection pooler
11434 Ollama ollama serve High — unrestricted LLM API
8000 Python MCP Server python -m mcpserver.server High — internal doc retrieval API
3001 Chart Server docker-proxy (mentorship-services-chartserver) Medium — WebSocket + API
9393 Mentorship Backend docker-proxy (mentorship-services-backend) Medium — mentorship platform API
9292 Orcta Stack Backend docker-proxy (orcta-stack-backend-1) Medium — core stack API
9696 Deno App docker-proxy (deno-app) Medium — email ping service
9999 PSS Backend docker-proxy (orcta-backend-pss) Medium — PSS API
4983 Drizzle Gateway docker-proxy (drizzle-gate) Medium — database gateway

Caddyfile Security Headers Gap

Site HSTS XFO XCTO Referrer-Policy Permissions-Policy
api.pss.orctatech.com
career.orctatech.com
mentorship.orctatech.com
email-ping.orctatech.com
ai.orctatech.com
bot.orctatech.com
orc-mcp.orctatech.com
files.ames.orctatech.com
waybills.orctatech.com
api.waybills.orctatech.com
ames.orctatech.com
api.ames.orctatech.com
pss.orctatech.com

SSH Exposure

SSH was listening on 0.0.0.0:22 via systemd socket activation (ssh.socket), reachable from the entire internet. Authentication relied solely on SSH keys (PasswordAuthentication no), but the attack surface was unnecessarily large.

Tailscale

The VPS was connected to a personal Tailscale (Adjanour@) with --advertise-exit-node, but this was underused — all traffic still went over the public IP.

Edit this page

On this page

Changes Made

1. Docker Compose Port Binding Fixes

Every exposed Docker container was fixed by prepending 127.0.0.1: to the host port mapping in its compose file, then recreating the container.

/srv/apps/ORCTA-BACKEND-PSS/docker-compose.yml

  app:
    ports:
-     - "9999:9999"
+     - "127.0.0.1:9999:9999"

/srv/apps/OrctaDocsMCP/docker-compose.yml

  postgres:
    ports:
-     - 5433:5432
+     - 127.0.0.1:5433:5432

  valkey:
    ports:
-     - 6379:6379
+     - 127.0.0.1:6379:6379

/srv/apps/mentorship-app-backend/apps/apiServer/docker-compose.yml

  app:
    ports:
-     - "9393:9393"
+     - "127.0.0.1:9393:9393"

/srv/apps/mentorship-app-backend/apps/chartServer/docker-compose.yml

  chartserver:
    ports:
-     - "3001:3001"
+     - "127.0.0.1:3001:3001"

/srv/apps/orcta-stack/docker-compose.prod.yml

  backend:
    ports:
-     - "9292:9292"
+     - "127.0.0.1:9292:9292"

/srv/apps/email-ping/docker-compose.yml

  deno-app:
    ports:
-     - "9696:9696"
+     - "127.0.0.1:9696:9696"

/srv/orcta-postgres/docker-compose.yml

  pgbouncer:
    ports:
-     - "6432:6432"
+     - "127.0.0.1:6432:6432"

Drizzle-Gate (no compose — recreated with docker run)

docker stop drizzle-gate && docker rm drizzle-gate
docker run -d --name drizzle-gate \
  --restart always \
  -p 127.0.0.1:4983:4983 \
  -e PORT=4983 \
  -v drizzle-gateway:/app \
  --network orcta_net \
  ghcr.io/drizzle-team/gateway:latest

2. Ollama — Systemd Service

/etc/systemd/system/ollama.service

- Environment="OLLAMA_HOST=0.0.0.0"
+ Environment="OLLAMA_HOST=127.0.0.1"

Then systemctl daemon-reload && systemctl restart ollama.

3. Python MCP Server — Code Fix

/srv/apps/OrctaDocsMCP/mcpserver/server.py

- mcp.run(transport="streamable-http", host="0.0.0.0", port=8000, path="/mcp")
+ mcp.run(transport="streamable-http", host="127.0.0.1", port=8000, path="/mcp")

Container rebuilt with docker compose build mcp and recreated.

4. Caddyfile — Security Header Overhaul

Added (security) snippet

Refactored all sites to use a shared snippet, eliminating duplication and adding missing Permissions-Policy.

(security) {
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        Referrer-Policy "strict-origin-when-cross-origin"
        Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=()"
    }
}

Then import security in every site block.

New site: gate.orctatech.com

gate.orctatech.com {
    reverse_proxy localhost:4983
    encode zstd gzip
    log { output file /var/log/caddy/gate.access.log format json }
    import security
}

Sites that received headers for the first time

  • waybills.orctatech.com
  • api.waybills.orctatech.com
  • ames.orctatech.com
  • api.ames.orctatech.com
  • pss.orctatech.com
  • gate.orctatech.com (new entry)

5. Tailscale — Migrated from Personal to Org

# Removed from personal tailnet (done by user in admin console)
# Reset state and re-authenticated to org
sudo systemctl stop tailscaled
sudo rm -f /var/lib/tailscale/tailscaled.state
sudo systemctl start tailscaled
sudo tailscale up --auth-key=<org-auth-key>
Before After
Personal: Adjanour@ Org: Orcta-tech@
IP: 100.120.2.25 IP: 100.125.3.44
Node: omarchy Node: vmi2813472

6. SSH — Locked Down to Tailscale

Systemd socket override

Created /etc/systemd/system/ssh.socket.d/override.conf:

[Socket]
ListenStream=
ListenStream=100.125.3.44:22
ListenStream=127.0.0.1:22
FreeBind=true

This replaced the default 0.0.0.0:22 and [::]:22 listeners.

iptables restriction

iptables -A INPUT -p tcp --dport 22 -s 100.109.232.127 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
iptables-save > /etc/iptables/rules.v4

Only 100.109.232.127 (laptop Tailscale IP) can connect to SSH.

Local SSH config

~/.ssh/config updated:

  Host orcta_vps
-   HostName 158.220.100.30
+   HostName 100.125.3.44

Additional: sshd_config safety net

/etc/ssh/sshd_config.d/10-tailscale-only.conf:

ListenAddress 100.125.3.44
ListenAddress 127.0.0.1

(Overridden by socket activation but kept as defense-in-depth.)

Edit this page

On this page

Lessons Learned

Root Cause: Docker’s Default Port Binding

The single biggest issue was that Docker, by default, publishes ports on 0.0.0.0 when no host IP is specified:

ports:
  - "6379:6379"     # Binds to 0.0.0.0 — publicly accessible!

vs.

ports:
  - "127.0.0.1:6379:6379"  # Only accessible from localhost

Lesson: Always use 127.0.0.1: prefix for internal services. The one container that had it correct (orcta-valkey-pss) was the template to follow — we just didn’t follow it everywhere.

Why This Went Undetected

  1. No regular port auditss -tlnp had never been run to check what was listening.
  2. Assumed safety in numbers — “It’s a VPS behind a firewall” (but Contabo doesn’t have a cloud firewall by default).
  3. Caddy configuration grew organically — each new subdomain was added manually, security headers were copy-pasted inconsistently, and no one noticed the sites without headers.
  4. SSH socket activation was invisible — changing ListenAddress in sshd_config had no effect because systemd’s ssh.socket was managing the actual bind. This wasted time debugging “why is SSH still on 0.0.0.0?”

What We’d Do Differently Next Time

Development practices

  • Use a compose linter or template that forces 127.0.0.1: on all non-public ports.
  • Add a pre-deploy check script that runs ss -tlnp and alerts on unexpected 0.0.0.0 listeners.
  • Use Caddy snippets from day one — the (security) snippet eliminated 50+ lines of duplication and made it easy to add headers to new sites.

Infrastructure

  • Tailscale first — the VPS should have been on the org tailnet from the start, with SSH bound to the Tailscale IP only. This would have made the port exposure less critical (though still not OK).
  • Infrastructure documentation — an up-to-date inventory of services, ports, and compose files would have made the audit much faster.
  • iptables baseline — a DROP rule for inbound ports that aren’t explicitly allowed should be part of the initial provisioning.

Security

Check Frequency Tool
Port scan (public) Weekly nmap from external host
Listening ports Per deploy ss -tlnp
Security headers Per deploy curl -sI https://domain
Docker port bindings Per PR docker ps --format
Tailscale peer list Monthly tailscale status

The One Thing That Almost Broke Everything

When we locked down SSH, the systemd socket activation meant that editing sshd_config.d/*.conf had zero effect on what address the SSH daemon listened on. The socket was managed by ssh.socket, not by sshd itself. This is a common Ubuntu 24.04 default that’s easy to miss.

Fix: Always check systemctl status ssh.socket before debugging SSH binding issues. And use either:

  • Socket drop-in: /etc/systemd/system/ssh.socket.d/override.conf
  • Or disable the socket: systemctl disable --now ssh.socket (then sshd manages its own socket)

Container Restart Surprises

When we changed compose files, docker compose up -d tried to recreate dependency containers (like Valkey in ORCTA-BACKEND-PSS) even though only the app service’s port binding changed. This caused a name conflict error. The fix was docker compose up -d --no-deps <service>.

Edit this page

Insights

Index

Edit this page

On this page

Operating Philosophies

These principles are inspired by proven product and engineering practices (including 37signals-style thinking) and adapted for Orcta’s context.

Build the small thing first

Start with the smallest version that solves the real problem. Ship fast, learn from usage, and expand only when the need is clear.

Opinionated defaults beat endless options

Give users a strong, sane path. Configuration comes later, after patterns emerge.

Calm software is a competitive advantage

Systems should reduce stress, not add to it. Prioritize clarity, stability, and predictability over flashy features.

Reduce scope, then improve depth

When timelines compress, cut features before cutting quality. A tight, reliable product earns trust faster than a wide, fragile one.

One team, one source of truth

Documentation is part of the product. Keep it current, concise, and close to the work so decisions scale with the team.

Make the system easy to explain

If you cannot explain a system in a few sentences, it is too complex. Simplify the architecture until it becomes teachable.

Edit this page

Quotes

A running collection of quotes that capture how we think and build. Add new entries as individual files in this folder and link them here.

Edit this page

Quote Template

Quote:

Author:

Context: <when/why the quote was said>

Edit this page

Notes

Short internal posts that capture lessons learned, decisions, and practical guidance.

Edit this page

Start small, earn trust

Tags: product, delivery, reliability

The fastest way to earn trust is to ship a small, reliable solution that solves the core problem. Bigger launches can wait; reliability cannot.

What this looks like at Orcta

  • Ship a minimum scope release with real users in mind.
  • Measure outcomes before adding new features.
  • Keep the product simple enough that any engineer can explain it.
Edit this page

Shipping with reliability

Tags: reliability, engineering, delivery

Reliability is the feature that keeps customers. A release is not done until it is stable under real usage.

What this looks like at Orcta

  • Add monitoring or logs before pushing a risky change.
  • Prefer one clear path over multiple half-finished options.
  • Treat post-release checks as part of the job, not an extra step.
Edit this page

When to say no

Tags: product, culture, delivery

Saying no protects the roadmap. Every yes should move a real metric or user outcome.

What this looks like at Orcta

  • Decline requests that do not match the mission or user need.
  • Reduce scope when a deadline is fixed.
  • Trade experiments for validated wins if time is tight.
Edit this page

Note Template

Title: <short, specific headline>

Date: YYYY-MM-DD

Summary: 2-3 sentences on the main takeaway.

Context

What prompted this note? Include relevant project or decision context.

Takeaway

What should the team remember or do differently next time?

  • Link to supporting docs, issues, or PRs.
Edit this page

Tags

Use short, consistent tags to make notes easy to scan.

  • product
  • engineering
  • delivery
  • reliability
  • infra
  • design
  • customer
  • culture
Edit this page
Last modified: