NestEggCode — a lightweight, self-hosted code browser
A tiny, dependency-free PHP application that gives you a GitHub-style browse-only view of your code, hosted on your own domain. Upload files (or whole folders, or a .zip) into named repositories from a simple admin panel, and share clean, readable URLs where visitors can browse the file tree, read syntax-highlighted source, and see your README.md rendered on the repo's front page.
It is not a version-control system. Think of it as a personal, self-hosted showcase for code you want to publish and link to — without a GitHub account, without Git, and without giving anyone else control over where your code lives.
Table of contents
- Features
- What makes it different from GitHub
- Limitations
- Server requirements
- Installation
- Project layout
- Security notes
Features
Browsing (public, no login required)
- Repository index at the site root listing every repository, with an auto-detected or manually set language badge and description.
- GitHub-style file tree with breadcrumbs, folder navigation, and persistent (even empty) folders.
- Syntax-highlighted source view for a broad set of languages (PHP, JavaScript/TypeScript, Python, Java, C/C++, Go, Ruby, SQL, Bash, CSS, HTML, JSON, Markdown, and more) via a small built-in, dependency-free highlighter.
- Rendered
README.mdon the repository root, using a built-in GitHub-Flavored-Markdown renderer (headings, tables, task lists, fenced code, images, links, blockquotes, and more) — no external Markdown library required. - Image rendering for PNG, JPG, GIF, SVG, WebP, BMP, ICO, and AVIF, with a Rendered / Source toggle for SVGs.
- Interactive 3D viewer for STL (binary + ASCII), 3MF, OBJ, PLY, and G-code — orbit / zoom / pan a model right in the browser (G-code renders as a layer-coloured toolpath). Powered by a small built-in WebGL viewer with no external 3D library — 3MF archives are even unzipped in-browser via the native
DecompressionStream. Text-based formats (OBJ, ASCII PLY, G-code) also keep a Rendered / Source toggle. - Raw view and Download buttons for every file (
/raw/served with correct MIME type andX-Content-Type-Options: nosniff). - Client-side file search in the file view's sidebar tree.
- Automatic language detection — a repo's dominant language is inferred from its files (documentation, license, and image files are ignored), or you can set the label manually.
- Generated social preview cards — every repository gets its own 1200×630 OpenGraph image (name, slug, description, language, file count and size) drawn server-side with GD and cached on disk, so links unfurl with a proper card on Slack, Facebook, X, LinkedIn and iMessage. The image URL carries a content hash, which is what makes crawlers pick up a card after the repo changes.
Admin (single user, password-protected)
- Create / edit / delete repositories, each with a slug, name, description, and optional language label.
- Slug renaming that moves the on-disk files and rewrites database paths in a single transaction.
- Drag-and-drop uploads of individual files or whole folders (directory structure preserved via the browser's directory-upload API).
- Zip import — upload a single
.zipand it is unpacked into the repository, recreating its folder structure. A single wrapping top-level folder (e.g.myrepo-main/) is stripped automatically, and macOS/Windows junk (__MACOSX/,.DS_Store,Thumbs.db) is dropped. - Manual folder creation, including nested paths (
src/utils/helpers). - Per-file and per-folder deletion (folder deletion cascades to everything inside it).
- Re-upload replaces a same-named file in place.
What makes it different from GitHub
| This app | GitHub | |
|---|---|---|
| Purpose | Publish & browse code you upload | Full source-control platform |
| Version control | None — files are just stored and shown | Full Git history, branches, diffs |
| Where it runs | Your own server / shared hosting | GitHub's cloud |
| Accounts | One admin (you); everyone else is a read-only visitor | Multi-user, orgs, teams |
| Collaboration | None (no PRs, issues, forks, comments) | PRs, issues, reviews, CI/CD |
| Dependencies | Zero — plain PHP + MySQL, no Composer/npm | N/A (hosted service) |
| Data ownership | 100% yours, on your host | Hosted by GitHub |
| Setup | Copy files, create a DB, edit one config file | Sign up |
| Cost | Whatever your hosting costs | Free / paid tiers |
In short: GitHub is a collaboration and version-control platform. This is a self-contained, self-hosted publisher — the smallest thing that gives your code a clean, GitHub-like public face on a domain you control, with no third parties and no dependencies to keep up to date.
Limitations
Be aware of what this project intentionally does not do:
- No version control. No commits, branches, history, or diffs. Uploading a file simply overwrites the previous one.
- No collaboration features. No issues, pull requests, forks, stars, comments, or multiple accounts.
- Single admin user. Credentials live in
config.php; there is no user-management UI or registration. - No Git integration. You cannot
git pushto it. Content is added through the web upload forms (files, folders, or a.zip). - Browse-only for the public. Visitors can read and download, but never edit.
- Basic syntax highlighting. The highlighter is a compact, regex-based tokenizer, not a full language parser — it colors one line at a time and covers common cases rather than every edge case.
- Simplified Markdown. The README renderer covers the common Markdown a README uses, but is not a full CommonMark implementation.
- File-size / count caps. Uploads are bounded per file, per request, and per zip (see upload limits); large archives are cut off once the total uncompressed size ceiling is reached.
- No search across repositories. File search is client-side and scoped to a single file view's tree.
- 3D viewer scope. The built-in 3D viewer covers STL, 3MF, OBJ, PLY, and G-code and needs a WebGL-capable browser. It does not handle glTF/GLB, STEP, FBX, or binary G-code (
.bgcode); materials/textures and mesh transforms are ignored (geometry is shown with flat shading). Very large models are bounded by the upload size cap.
Server requirements
- PHP 7.4 or newer (uses typed function signatures,
??, arrow-ish syntax, etc.). PHP 8.x recommended. - MySQL / MariaDB with the PDO MySQL extension enabled.
- Apache with
mod_rewrite(an.htaccessfront-controller rule ships with the app). Other web servers work too, but you'll need to translate the rewrite rule (route all non-file, non-directory requests toindex.php). - PHP
zipextension — optional, required only for the "Upload a zip" feature. Everything else works without it. - PHP
gdextension with FreeType support — optional, required only for the generated social preview cards. Without it, pages simply advertise noog:imageand everything else works unchanged. - Standard PHP file/session functions (
session,fopen/fwrite,move_uploaded_file,password_hash/password_verify,random_bytes). - A writable
uploads/directory. - No extra PHP extension for 3D viewing — STL/3MF/OBJ/PLY/G-code are parsed and rendered entirely client-side in a WebGL-capable browser; the server just stores and serves the raw file.
Works out of the box on typical cPanel shared hosting and locally with MAMP / XAMPP / WAMP.
Installation
Easiest: the guided installer (recommended)
Upload the project to your server, then visit https://your-site/install.php in a browser (or .../pgh5/install.php in a subfolder). The installer:
- Runs server checks (PHP version, database driver, writable folders) and explains how to fix anything that fails.
- Asks for your database and admin details, then tests the connection, creates the tables, writes
config.php, and matches the.htaccessURL settings — all for you. - Shows a post-launch checklist to confirm everything works.
When you're done, the admin dashboard shows a banner offering to delete install.php (only after you tick a box confirming your site works — it never deletes anything on its own). Remove install.php once the site is live — leaving it in place is a security risk.
Prefer to do it by hand? Follow the manual steps below instead.
Manual installation
1. Get the files onto the server
Copy the project into your web root (or a subfolder of it). For example:
- Production (domain root): upload everything into
public_html/. - Local MAMP: drop the folder into
htdocs/(e.g.htdocs/pgh5).
2. Create the database
Create an empty MySQL database and a user with access to it (in cPanel: MySQL Databases; locally: phpMyAdmin). Then import the schema:
mysql -u YOUR_USER -p YOUR_DB < schema.sql
…or import schema.sql through phpMyAdmin. It creates three tables: repositories, files, and folders.
3. Configure config.php
Open config.php and set your real values:
define('DB_HOST', 'localhost');
define('DB_NAME', 'your_database_name');
define('DB_USER', 'your_database_user');
define('DB_PASS', 'your_database_password');
⚠️ Keep
config.phpout of version control and out of public reach. It holds your database credentials and admin password hash. The bundled.htaccessalready denies direct web access toconfig.phpandschema.sql, but you should still never commit real secrets to a public repo. Consider addingconfig.php,uploads/, and any*.zipto a.gitignorebefore pushing this project to GitHub.
4. Set your admin password
The admin password is stored as a bcrypt hash, never in plaintext. Generate one:
php -r "echo password_hash('your-strong-password-here', PASSWORD_DEFAULT), PHP_EOL;"
Paste the result into config.php and pick a username:
define('ADMIN_USER', 'your-admin-username');
define('ADMIN_PASS_HASH', '$2y$12$...the-hash-you-generated...');
The shipped default hash is for the password
changeme— change it before going live.
5. Point the router at the right base path
BASE_PATH is the URL prefix the app is served under.
- Domain root:
define('BASE_PATH', ''); - Subfolder (e.g. local
htdocs/pgh5):define('BASE_PATH', '/pgh5');
Match this to the RewriteBase in .htaccess (defaults to /). If you deploy in a subfolder, set RewriteBase /pgh5 accordingly.
6. Set the public origin (recommended)
SITE_URL is the site's scheme and host with no trailing slash and no BASE_PATH, e.g. define('SITE_URL', 'https://code.example.com');.
Social crawlers reject relative og:image / og:url values, so share metadata has to be absolute. Left empty, the app derives the origin from the request's Host header — fine locally, but set it explicitly in production, since a client can send any Host it likes and behind a proxy the scheme may be guessed wrong.
7. Adjust upload limits (optional)
Two places govern upload sizes:
config.php— application-level caps:MAX_FILE_SIZE— per-file limit (default 25 MB), also applied to each file extracted from a zip.MAX_ZIP_TOTAL— ceiling on the total uncompressed size of one zip import (default 200 MB), a guard against zip bombs.
.user.ini— PHP-level caps (upload_max_filesize,post_max_size,max_file_uploads).
Note:
.user.iniis read by PHP-FPM / CGI / FastCGI (how cPanel typically runs PHP) but is ignored when PHP runs as an Apache module (common in MAMP). If your uploads are being truncated locally, set the same values inphp.iniinstead, or addphp_valuelines to.htaccess.
Make sure uploads/ is writable by the web server.
8. Log in and create your first repository
- Visit
https://your-domain/admin/login.php(or.../pgh5/admin/login.phpin a subfolder). - Log in with the username and password you configured.
- From the Dashboard, create a repository (give it a slug like
save-utilsand a name). - Click Upload to add files, a folder, or a
.zip. - Your repo is now public at
https://your-domain/your-slug.
Project layout
.
├── install.php # Guided web installer (delete after setup)
├── index.php # Front controller / router
├── repo.php # Public repository views (tree / blob / raw)
├── config.sample.php # Safe-to-commit config template
├── config.php # Your credentials & settings (keep private)
├── schema.sql # Database schema (import once)
├── new-password.php # Helper for generating a password hash
├── .htaccess # Rewrite rules + protects config.php / schema.sql
├── .user.ini # PHP upload limits (FPM/CGI hosts)
├── admin/ # Password-protected admin panel
│ ├── login.php
│ ├── dashboard.php # Create / edit / delete repositories
│ ├── upload.php # File / folder / zip uploads, folder creation
│ └── delete.php
├── api/
│ └── highlight.php # Dependency-free syntax highlighter
├── includes/
│ ├── db.php # PDO connection + CRUD (prepared statements)
│ ├── auth.php # Sessions, login, CSRF
│ ├── helpers.php # URL/slug/path sanitising, language detection
│ ├── tree.php # File-tree building & rendering
│ ├── markdown.php # GitHub-Flavored-Markdown → HTML
│ ├── og.php # Generated social preview cards (GD)
│ ├── header.php
│ └── footer.php
├── assets/
│ ├── style.css
│ ├── model-viewer.js # Dependency-free WebGL viewer for STL/3MF/OBJ/PLY/G-code
│ ├── og-default.png # Share card for non-repository pages
│ └── fonts/ # Bundled DejaVu faces used to draw the cards
└── uploads/ # Stored repository files (writable)
└── og/ # Cached social cards (auto-created, safe to delete)
Security notes
The app is built with a few defensive measures worth knowing about:
- Prepared statements everywhere — all database access uses PDO with
PDO::ATTR_EMULATE_PREPARES => false. - CSRF protection on every admin POST (token compared with
hash_equals). - Path traversal protection — uploaded filenames and paths are sanitized (
.., absolute paths, and unsafe characters are stripped), and served files are re-resolved withrealpathand confirmed to live inside the repo directory before being read. - Safe zip extraction — the app streams each entry through
ZipArchive::getStream()rather thanextractTo(), so archive-controlled paths can't escape the target folder; oversized entries and zip-bomb totals are rejected. - Bcrypt password hashing and session regeneration on login.
config.phpandschema.sqlare denied direct web access via.htaccess.
For production, always serve over HTTPS, use a strong admin password, and keep config.php out of any public repository.
Built with plain PHP and MySQL — no frameworks, no Composer, no build step.