# Delta-V hosting — build rules for this website

Read this before writing any code for this site. It describes what this
server does, and does not allow. Anything outside these rules will fail
on this machine even though it works locally.

Public copy: **https://delta-v.cn/docs/AGENTS.md**

Your student uploads files by hand through a web page — see section 9
before you tell them anything about getting the code onto the server.

---

## 1. Everything lives in `public_html/`

The whole website is the `public_html/` folder. Nothing above it is
published, and PHP is restricted to it by `open_basedir`.

- **Never generate a path that escapes it.** `require __DIR__ . '/../config.php'`
  or `require '../includes/bootstrap.php'` will not resolve and the page
  fails with a **500**, not a helpful error.
- Keep every file the site needs inside the folder being served.
- There is no build output directory, no `dist/`, no `vendor/` expected
  by the web server — serve what is in the folder.

## 2. Server-side code is PHP, and only PHP

- Scripts run under **PHP-FPM**: `index.php`, `api.php`, and so on.
- **No Node.js, no Python, no Ruby, no other runtime.** Do not propose
  `package.json`, `npm start`, `npx`, `pip`, `requirements.txt` as a
  runtime, Express/Next/Vite dev servers, or anything that listens on a
  port — none of it runs here.
- **No long-lived processes.** Nothing can be started, supervised, queued
  or kept alive between requests. Assume every request starts fresh.
- **No server-side build step.** A bundler/transpiler/Minifier must not be
  required for the site to work. Static assets are uploaded as-is.
- Client-side JavaScript is fine: React/Vue/vanilla served as static
  files works, as long as nothing needs to be compiled on the server.

## 3. Data is SQLite, not MySQL

- Create a `.sqlite` (or `.db`) file **inside the site folder** and open
  it with PHP's PDO:

  ```php
  $pdo = new PDO('sqlite:' . __DIR__ . '/data.sqlite');
  ```

- **There is no MySQL, Postgres, or hosted database account.** Never
  emit `mysqli_connect`, `PDO('mysql:...')`, connection strings, or
  credentials for a database server.
- The database file is not downloadable over HTTP, but PHP reads it
  normally. Keep it in the folder; back it up by downloading the folder
  (the DB file is excluded from HTTP but included in the ZIP).

## 4. Where an `index.html` is required

Directory listing is disabled, so a folder without an `index.html`,
`index.htm` or `index.php` returns **403** to visitors.

**An index is required in exactly two places:**

1. **The root of the site** — always.
2. **The root of a separate sub-project** — a folder that is itself a
   small website, with its own pages and its own css/js.

**Every other folder is an asset folder and must not get an index:**
`css/`, `js/`, `images/`, `icons/`, `fonts/`, `data/`, `docs/`, and any
folder the parent page only references *as a file* (`href="style.css"`).
A 403 when somebody browses into an asset folder is correct and expected —
it is not a bug, and adding an index there would expose files that were
meant to stay unlisted.

**Never recommend "add an index.html" to every directory.**

Tell the two apart using the **Folder signals** section of the digest:

- `index=yes` — already has one. Never suggest adding another.
- `denied=yes` — the folder protects itself with its own `.htaccess`.
  Private. Say nothing about it.
- `linked=yes` with only css/js/image contents — an asset folder. Say nothing.
- No `index`, not `denied`, contents include `.html`/`.php`, and the
  homepage links into it — this looks like a sub-project missing its
  index. Worth reporting.
- No `index`, contents are only docs/images/data — not a project. Say nothing.
- **The site root has no index — always report that one.**

## 5. Names: no spaces, kebab-case only

- **No spaces in file or folder names.** Use `my-blog`, not `My Blog`.
  Spaces break URLs and PHP routing on this server.
- No non-ASCII or unusual punctuation in names either.
- Linux is **case sensitive**: `index.html` is not `Index.html`, and a
  link written as `/Style.css` will 404 if the file is `style.css`.
- Only `.htaccess` and `.well-known` are allowed as dot-prefixed names;
  `.git`, `.env.local` and similar cannot be created through the panel.

## 6. What is blocked from HTTP download

These return **403** if requested over HTTP:

```
sql sqlite sqlite3 db db3 mdb dump bak old orig save
swp swo swn env pem key crt der p12 pfx p7b p7c ppk jks
ini conf cfg config yml yaml toml properties htpasswd log err
```

plus any `*.php.<something>` and files ending in `~`.

PHP still reads all of them normally, so a SQLite file, an `.env` and a
`.php` include are perfectly usable server-side. This only stops browsers
from fetching them.

## 7. `.htaccess` works — and can break the whole site

`AllowOverride All` is enabled, so `.htaccess` rules are honoured.

- **One syntax error returns a 500 for every page on the site.**
- Test the site immediately after changing it, and keep a copy of the
  previous version.

## 8. Limits

- **250 MB** storage per site. Over quota pauses the site.
- HTTPS on `*.delta-v.cn` is automatic — no certificate work needed.
- Account suspension/deletion dates are set by the teacher.

## 9. How the student actually gets files onto the server

**Your student cannot use FTP, SFTP, `scp`, `rsync`, `git push`, a shell,
or any command line.** The only way onto this server is the file manager
at **https://delta-v.cn** — the *Upload files* tab, which accepts a file
drop, a whole dropped folder, or a `.zip`.

**Never tell a student to run a deploy command.** No `git clone`, no
`npm run deploy`, no `rsync`, no `scp`, no `ssh`. It will not work and it
will read as nonsense to someone who has never opened a terminal. Your
job is to produce a finished folder of files and tell them, in plain
steps, how to drop it in.

**The student's site root is their own address**, `username.delta-v.cn`
— the folder you are editing is `public_html/`, served at the top level
of that domain. `https://umi.delta-v.cn/index.html` is the file
`public_html/index.html`.

### Uploads land in the folder you are looking at

The file manager uploads into **whichever folder is currently open** in
the *My files* tab. There is no "choose destination" step. This is the
single most common reason a student ends up with a broken site, so be
explicit about it.

**A dropped folder keeps its name.** Dropping `my-project/` while the
root is open creates `public_html/my-project/` — it does **not** scatter
its contents into the root. The student's site then still has whatever
was there before, and the new project is one level too deep.

So there are exactly two correct shapes. Tell the student which one they
are doing, and say it before they upload.

### Option A — the project is the whole site (most common)

`index.html` must end up **directly** in the site root, so
`https://username.delta-v.cn/` shows it.

1. Open the site root — the top of the *My files* tab, with no folder
   open.
2. Delete what is there. Tick the boxes, then **Delete selected**, or
   use **Delete everything here**. There is no trash can, so say so.
3. Open the *Upload files* tab.
4. Drop in the project's **contents**. The neatest way: zip the files
   *inside* the project folder — so `index.html` is at the top of the
   zip, not inside a folder — and upload that zip. It is unpacked into
   the site root.

**Recommend the zip.** It is one file, one drop, and the folder structure
survives. If `index.html` is at the top level of the zip, not inside a
folder named after the project, the site works. Zipping the project
*folder* instead is the same mistake one step removed: it reproduces
`public_html/my-project/index.html` and the root stays empty.

### Option B — the project goes in its own folder

Right when the student already has a site at the root, or wants to keep
two projects side by side.

1. In the *My files* tab at the site root, press **New folder**. Name it
   in kebab-case, no spaces: `my-project`, `week-3`, `photo-gallery`.
2. **Open that folder** so the file manager is looking inside it.
3. *Upload files* tab, drop the project in. It lands in the folder you
   opened, not the root.
4. That folder now needs its **own** `index.html` at its top, or visitors
   get a 403. The parent's links should be relative: `href="my-project/"`.

### What to tell the student, in this order

Students using this have no technical background, so avoid jargon and
give one path, not options. If you are unsure which option they want,
recommend **A** — one project at the site root is the least to go wrong.

- Name the exact file that must land at the top (`index.html`).
- Say whether to select the folder's *contents* or the folder itself —
  this is the decision that breaks the site.
- Mention the delete is permanent, before they delete anything.
- Say where to look afterwards: their own address,
  `https://username.delta-v.cn/`, and a hard refresh because the browser
  caches.

---

## Checklist before you finish

- [ ] No `../` anywhere in `require`/`include`/file paths
- [ ] No Node/Python/runtime assumptions; PHP only
- [ ] Data via SQLite + PDO, no MySQL
- [ ] `index.html` (or `index.php`) in the site root **and** in the root of any
      sub-project — never in asset folders
- [ ] No spaces in any file or folder name; links match the real case
- [ ] Nothing you rely on is in the blocked-download list above
- [ ] `.htaccess`, if touched, was tested afterwards
- [ ] Site loads from a cold cache in a browser with no console errors
- [ ] Told the student how to upload it, using the file manager only — no
      `git push`, `scp`, `rsync` or shell commands
- [ ] Said which shape they are using (site root vs its own folder), and
      that a zip needs `index.html` at the top level
- [ ] Said the delete is permanent, and where to check the live site after

## If you are unsure

The teacher is at **info@billyhau.hk**. Prefer asking over shipping
something that returns a 500 or a 403.
