Nathan's IT and Cybersecurity Journey
From learning fundamentals to building and securing real applications.
Nathan's Technology Wiki is a personal knowledge base for documenting my growth in information technology, secure software development, cybersecurity, AI, and responsible security testing.
This is where I turn lessons into plain-English notes, keep track of projects, and connect what I learn to practical work. It is a living record—updated as my skills and interests evolve.
My journey
I am building a broad, hands-on foundation in technology. That means learning the systems behind the screen—networks, operating systems, code, cloud services—and then asking how each one can be built and used more safely.
My goal is not just to collect tools or certifications. I want to understand the reasoning behind good technical decisions: how an application handles user data, why a network is segmented, how a scanner finding becomes a fix, and where AI can genuinely help.
Learning topics
This wiki is organized around the skills I am actively developing.
Vibe coding
Vibe coding is my approach to building quickly with AI while still owning the thinking: defining the goal, shaping the experience, reviewing every change, and documenting what happened. AI can accelerate a build; quality comes from the checks around it.
3D image project
My 3D image project is a study in turning an idea into a visual experience. The focus is not only on making an image look good, but also on composition, performance, loading behavior, responsive presentation, and the way a visitor understands the work.
- Make the creative goal and audience clear before prompting or modeling.
- Prototype small, then test the image on different screen sizes and connection speeds.
- Keep original assets organized and name versions consistently.
- Review the final result for visual clarity, accessibility text, and page performance.
OpenTrail
OpenTrail is a project space for documenting how an idea moves from an early concept to a dependable product. It gives me a place to record decisions, track changes, test the user journey, and explain why a version was released.
Code changes and best practices
Every change should be small enough to understand and easy enough to reverse. I use descriptive names, keep secrets out of code, validate inputs, update dependencies carefully, and test the behavior that users will actually touch.
Preflight checks
- Does the change solve a specific user need?
- Did I review the changed files and remove debug code?
- Are secrets, keys, and personal data protected?
- Do links, forms, loading states, and error states work?
- Does the UI work on desktop and mobile?
Postflight checks
- Did the release match the planned version and changelog?
- Can I complete the main user journey without confusion?
- Do logs or monitoring show an unexpected error?
- Is the site fast enough with the new assets?
- What did I learn for the next version?
Versioning, UI, and UX
Versioning gives every meaningful release a name and history. UI is what the interface looks like and how controls are arranged. UX is how the whole experience feels—from the first click to a successful outcome. I treat all three as part of the same quality standard.
v0.3.0 — Added 3D gallery view
What changed: added an optimized visual gallery and responsive layout.
Why: makes the project easier to explore on any device.
Checked: navigation, image alt text, load time, and mobile layout.
Network & homelab
This is a sanitized operating record of my local lab. The current inventory shows 23 running containers across 11 named workloads on one local Docker host. IP addresses, hostnames, credentials, and exact proxy destinations are intentionally left out of this public-facing wiki.
How the setup works
Nginx Proxy Manager is currently attached to its own proxy network and the OpenLinks network. The wiki and OpenTrail use separate application networks, so any proxy reachability must be configured deliberately rather than assumed. This page documents the design boundary, not private routing details.
Running applications
| Application | What it does | Stack | Known status / duration |
|---|---|---|---|
| Technology Wiki | Local knowledge base for my IT and cybersecurity journey. | Vinext, React, Next.js, Docker | Running; created during this wiki build. |
| Nginx Proxy Manager | Reverse proxy and certificate-management entry point for approved web services. | Nginx Proxy Manager, Docker | Running; observed about 13 hours at inventory time. |
| OpenTrail | Outdoor trail web application with a documented product and release workflow. | Web app, API, PostGIS, Redis, MinIO, migration job | Running; historical record includes release v0.3.0. Current runtime observed about 13 hours. |
| OpenLinks | Link and knowledge workspace. | Node.js app, PostgreSQL, MinIO | Healthy; current runtime observed about 13 hours. |
| WeaveNote | Personal notes and workspace application. | Node.js app, PostgreSQL, Adminer | Healthy; current runtime observed about 13 hours. |
| Cinderstrike Security | Security project application and supporting data service. | Node.js app, PostgreSQL | Healthy; current runtime observed about 13 hours. |
| Shadowbroker | OSINT workspace for tracking global events, aircraft, satellites, and related signals. | Node.js frontend, Python API | Healthy; current runtime observed about 13 hours. |
| Apache Guacamole | Browser-based remote-access gateway for approved systems. | Guacamole, guacd, PostgreSQL | Running; current runtime observed about 13 hours. |
| Krawl honeypot | Controlled security monitoring and deception-lab service. | Python service | Running; current runtime observed about 1 hour. |
| Audiobookshelf | Personal audio and media library. | Audiobookshelf, Docker | Running; current runtime observed about 13 hours. |
| Portainer | Container-management dashboard for the local Docker host. | Portainer CE, Docker | Running; current runtime observed about 13 hours. |
Databases, object storage, Redis, guacd, and Adminer are supporting services within these workloads, not separate user-facing applications. A running-container count is not the same as a physical host count: this inventory confirms one Docker host, while the total number of devices on the internal network is not documented here.
Past, one-time, and stopped work
Limits, hurdles, and what I learned
Operational limits
- Service groups are intentionally isolated, so proxy access needs an explicit route or shared network.
- Container uptime is a runtime snapshot, not proof of when a project began.
- Stateful services need tested backups, health checks, and careful update planning.
- Admin tools should remain trusted-only and should not be exposed publicly by default.
Current hurdles
- One local host runs multiple workloads, so CPU, memory, storage, patching, and monitoring must be managed together.
- BiasLens needs evidence-based troubleshooting before it is brought back online.
- Exact reverse-proxy mappings require a separate private configuration record.
- Project timelines need repository first-commit dates or release notes before they can be stated precisely.
I record architecture and lessons here, but keep secrets, internal addresses, firewall rules, and remote-access details in a protected administrator-only location.
Secure coding
Secure coding is the practice of considering security throughout an application's life—not just after something breaks. My notes cover input validation, authentication, secrets management, dependency updates, error handling, logging, and code review.
A secure app protects people as well as systems. I look for practical ways to reduce risk without making software harder to use.
Application security
I learned that application security starts before code: identify what data the app handles, who should be allowed to use each action, where information crosses a trust boundary, and what happens when a request fails. That turns security into design work rather than a last-minute patch.
Build habits
- Validate input at the boundary and return safe, useful errors.
- Keep dependencies updated and review changes before merging.
- Minimize data collected, displayed, and retained.
Review questions
- Can an unauthenticated user reach this action?
- Can a valid user access another user's data?
- Does an error reveal internals, secrets, or stack traces?
JWT tokens
JSON Web Tokens are signed credentials, not a substitute for authorization. I learned to keep tokens short-lived, verify their signature and expected issuer, audience, expiry, and algorithm on every protected request, and make authorization decisions from trusted server-side rules. Tokens must never contain passwords, secrets, or unnecessary personal data.
Hashing and encryption
Hashing passwords
Passwords are one-way protected values. I use a purpose-built password-hashing algorithm such as Argon2id or bcrypt with a unique salt, never a fast general-purpose hash alone.
Encrypting data
Encryption protects data that must later be read. I use established libraries and TLS for data in transit, separate keys from data, and plan for key access, rotation, and recovery.
User management
User management taught me that a login is only the beginning. Each account needs a clear lifecycle: create, verify, assign the minimum role, change access when responsibilities change, disable promptly, and record important security events. Roles should describe tasks, while permissions control exactly what a role can do.
Database management
Databases hold the information that makes an app valuable, so I learned to treat them as private dependencies. Applications use separate, restricted database accounts; migrations are reviewed; backups are tested; and database administration is kept off the public internet. The app should receive only the database permissions required for its own tables and actions.
SQL injection defense
SQL injection is prevented by separating data from commands. I use parameterized queries or a trusted ORM, validate input types and limits, avoid building queries through string concatenation, and give database accounts minimal privileges. Testing includes malformed and unexpected inputs so failures are safe and visible to the development team, not exploitable by a user.
Hardening APIs
API security combines strong request validation with clear authorization. I learned to authenticate protected endpoints, check permissions for every object and action, enforce schemas and size limits, rate-limit sensitive routes, minimize response data, use safe error messages, protect secrets in environment-managed configuration, and review CORS rules intentionally.
Before release
- Confirm every endpoint has the correct access rule.
- Test invalid input, missing tokens, and cross-user access attempts.
- Verify logs help diagnose issues without recording secrets.
After release
- Watch authentication failures and unusual error rates.
- Patch vulnerable dependencies and rotate exposed credentials.
- Review permissions as the API gains new features.
Permissions for other containers
Container security is also application security. Each service should run as a non-root user where practical, avoid privileged mode, use only the capabilities and mounted volumes it needs, and communicate over intentional Docker networks. Databases and admin tools should not be exposed by default, and inter-service credentials should be narrowly scoped and kept out of images and source control.
Vulnerability scanning and pentesting
Vulnerability scanning helps identify potential weaknesses. Penetration testing validates risk through carefully scoped testing. Both require clear authorization, a defined scope, and responsible reporting.
My study notes focus on asset discovery, scanner results, false-positive triage, remediation, evidence, and how to communicate findings so they can be fixed.
Project log
Large projects now have their own wiki articles so architecture, releases, security lessons, roadmaps, and limitations can grow without overloading the main page.
Outdoor trail application, modular stack, release history, migrations, and operational lessons.
OSINT application with separate frontend, Python API, data pipelines, and deployment assets.
Notes workspace, authentication hardening, database management, and delivery lessons.
Planning, architecture & delivery
My projects taught me that reliable software starts with a plan and ends with evidence. The goal is to design the user journey, data boundaries, security controls, and recovery path before writing a large amount of code—then make each release traceable and testable.
Plan and architect first with advanced models
Advanced reasoning models are useful planning partners when I give them a clear product brief, not a vague request to build everything. I use them to challenge assumptions, draft alternatives, identify missing user states, propose a data model, and turn a feature into testable acceptance criteria. I keep the final decisions, security review, and code ownership human-led.
Non-monolithic code
Non-monolithic does not have to mean many servers. It means code is organized around clear responsibilities so one feature does not become a single, tangled file or application layer. A modular monorepo can still deploy as one product while keeping UI, domain logic, API routes, data access, and shared types separate.
Module boundaries
- Presentation: pages, components, accessibility, and user states.
- Domain: business rules and use cases that do not depend on a screen.
- Infrastructure: database, storage, third-party APIs, and container configuration.
- Shared packages: stable contracts, types, validation, and reusable utilities.
Why it matters
- Smaller changes are easier to review, test, and roll back.
- Permissions and secrets stay closer to the service that truly needs them.
- A failure or redesign is less likely to spread across unrelated features.
- Teams or future contributors can work without hidden coupling.
Versioning and change tracking
I use a changelog as the plain-English record of what changed and why, and tags/releases as the exact version that was delivered. Semantic versioning gives a useful shared language: a patch for compatible fixes, a minor version for compatible features, and a major version when the contract changes. Every release should point to its source revision, migration impact, known limitations, and rollback plan.
Delivery pipelines
A pipeline is the repeatable path from a code change to a verified release. I learned that pipeline visibility matters as much as speed: a failing install, test, or migration must stop the release and show the real error. Silent success is not success.
Detailed preflight checks
Product & architecture
- Confirm the user story, success condition, non-goals, and accessibility states.
- Review data flow, system boundaries, API contract, and dependency ownership.
- Choose a module boundary before adding code; avoid putting UI, business logic, and database access in one place.
- Write a migration and compatibility plan before changing persisted data.
Security & operations
- Check authorization on every protected action and object, not only at login.
- Confirm secrets come from protected configuration and never from committed files or images.
- Review exposed ports, proxy routes, container privileges, volumes, and network reachability.
- Verify backups, health checks, observability, resource limits, and a safe rollback path.
Release quality
- Run the build, type checks, tests, and targeted manual user journeys.
- Review the complete diff for debug code, unused permissions, and accidental scope expansion.
- Update version, changelog, release notes, and known limitations.
- Make sure pipeline failures stop the process rather than hiding an upstream error.
Detailed postflight checks
Verify the experience
- Complete the highest-value user journey as a real user would.
- Test login, logout, permissions, error states, and a cross-user access boundary.
- Confirm the new version and user-facing changes match the approved release notes.
Verify the platform
- Check service health, startup logs, error rate, latency, storage, and resource use.
- Confirm migrations completed and backups are still scheduled and recoverable.
- Watch API authentication failures, unusual access patterns, and container restarts.
Close the learning loop
- Record what went well, what surprised me, and what should become a standard check.
- Create follow-up work for defects, hardening, documentation, and monitoring gaps.
- Keep the rollback plan until the release is stable, then close the delivery record.
What my projects taught me
| Project evidence | What I learned to include next time |
|---|---|
| OpenTrail is organized with apps, packages, documentation, infrastructure, release tags, and a semantic-versioning changelog. | Start with intentional module boundaries, document architecture and release policy, and make each version traceable to a decision and a tested build. |
| Shadowbroker separates frontend, backend, and deployment assets, including Helm configuration. | Keep presentation, API, and deployment concerns separate so each can be tested, secured, and changed without turning the product into one monolith. |
| WSH records a JWT configuration health check and a build issue where an output pipeline masked an install failure. | Fail closed on unsafe authentication configuration, test the health of security-critical dependencies, and preserve real command failures in automated pipelines. |
| OpenLinks uses Prisma migrations, backup artifacts, and a restore workflow. | Plan schema changes, backups, and recovery alongside features—database operations are part of delivery, not an afterthought. |
Roadmaps, stack design & app behavior
Future planning is how I turn a good idea into work that can be delivered safely. I learned to plan the outcome, dependencies, architecture, backup and recovery needs, and user flow together—not as separate tasks added after the application already exists.
Roadmaps and future planning
A roadmap is a direction and sequence, not a promise that every feature will ship on a specific day. I organize it by outcomes: what user problem is being improved, what evidence would show success, what dependencies must be ready, and what could make the work unsafe or too expensive.
Now: discover
- Define the problem, user, success measure, scope, and non-goals.
- List risks, data involved, integrations, and owners.
- Use an advanced model to challenge the brief and identify missing states or edge cases.
Next: design & deliver
- Choose the smallest architecture that meets the need.
- Break the work into milestones with acceptance checks and dependencies.
- Build a thin vertical slice first: UI, API, data, and permissions working together.
Later: stabilize & improve
- Use release evidence, feedback, errors, and performance data to prioritize.
- Schedule upgrades, backup restore drills, security reviews, and debt reduction.
- Move uncertain ideas forward only after the current foundation is healthy.
Stack and backup design
A stack is the set of layers that make an application work: interface, application/API, identity, database, cache or queues, file/object storage, observability, network entry, and deployment. I choose each layer based on a real requirement and document the owner, data it handles, access it needs, and how it is recovered.
How an application interacts and functions
Before building a feature, I map the path of one normal request and one failure. The diagram below is the mental model I use for a typical local web application; actual components vary by project.
Interaction contract
- Every boundary has an expected request, response, failure behavior, and owner.
- UI convenience checks never replace server-side validation or authorization.
- Services use explicit APIs rather than reaching into another module's private data.
Design for failure
- Define timeouts, retries, idempotency, and a clear user message for failed requests.
- Keep changes reversible through versioned artifacts and migration/rollback plans.
- Use health checks and restore tests to prove the system can recover.
Quality, testing & cybersecurity framework
I learned that building an application is not the same as proving it works. Quality comes from checking the product against its requirements, validating it with real user journeys, and operating it inside a security framework that can prevent, detect, respond to, and recover from problems.
Validation and verification
Verification: built correctly
Verification checks the implementation against the agreed design and acceptance criteria. I use code review, type checks, unit and integration tests, API contract checks, migration review, dependency checks, and configuration review to prove the release matches its specification.
Validation: built the right thing
Validation checks the real outcome. I complete the user journey, confirm the problem is actually solved, test accessibility and error states, and collect feedback or operational evidence. A technically correct feature can still fail validation if users cannot use it safely or clearly.
Testing to ensure an app works well
My holistic cybersecurity framework
My framework connects the full lifecycle of an application and lab environment. It is a practical set of habits I can apply to any project, not a claim that a tool or container is secure by itself.
Containers for isolated troubleshooting
Containers let me package an application with its runtime dependencies and run it separately from other workloads. This makes it easier to reproduce a problem, compare configuration, inspect one service's logs, restart or rebuild a single app, and reduce the chance that a dependency change breaks every other project on the host.
What isolation helps with
- Separate application dependencies, environment configuration, volumes, and logs.
- Recreate a known version of one workload without reinstalling the entire host.
- Limit service-to-service reachability through intentional Compose networks.
What it does not solve alone
- A container is not automatically a complete security boundary.
- Privileged containers, broad mounts, exposed ports, and shared secrets can still create risk.
- Host patching, backup protection, least privilege, and monitoring remain necessary.
Nginx Proxy Manager: multiple apps from one host
Nginx Proxy Manager (NPM) gives one host a controlled front door for multiple web applications. It receives web requests on the standard HTTP/HTTPS entry points, matches the requested hostname to an approved proxy-host rule, terminates TLS when configured, and forwards the request to the correct internal application. This allows several apps to share one host without each app directly owning the public web ports.
About this wiki
This is a personal reference site inspired by the clear, article-based structure of traditional wikis. It is independent and not affiliated with Wikipedia or the Wikimedia Foundation.
This page is a work in progress. Last updated: July 2026.