Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,17 @@ httpx->http_printf(httpc, "Content-Type: text/html\r\n\r\n");

In HTTPD 4.0.0, CGI registration moves from Lua defaults to Parmlib-only. No CGIs are active unless explicitly configured.

**A module runs in HTTPD's task, so `samplib/httpd` is part of the modules'
contract, not just the server's.** The LINK SVC dispatches into this task; a
module has no allocations of its own and opens any ddname against the STC's.
`HASPCKPT` / `HASPACE1` are there for exactly that reason — HTTPD never touches
them, mvsMF's jobs API opens both through libc370's `jesopen()`. They were
dropped in 4.0.0 on the reasoning that "nothing else in the server opens them",
which was measured against `httpd/src/` alone and missed the three repos whose
modules run in this address space; issue #256. **Before removing anything from the PROC, grep `../mvsmf`,
`../httplua` and `../httprexx` too** — a `grep` that stops at this repo's edge
cannot answer the question.

### HTTP/1.1 Design (implemented)

**Response body framing — decision logic:**
Expand Down
104 changes: 84 additions & 20 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,101 @@ stops. `CLAUDE.md` forbids a task list in itself because a copy of a tracker is
wrong the first time someone closes something, and the only defence that works
is to hold nothing worth going stale.

*Last reconciled against the tracker: 2026-08-24, three issues open (#252 filed
and closed the same day by PR #253; before it #250, #237 by PR #249, #245 by
PR #248, #233 by PR #244, #242 by PR #246 and #243 by PR #247).*
*Last reconciled against the tracker: 2026-08-25, five issues open (#256 filed
that day with PR #257; #254; before them #252 filed and closed 2026-08-24 by
PR #253, #250, #237 by PR #249, #245 by PR #248, #233 by PR #244, #242 by
PR #246 and #243 by PR #247).*

---

## The order

| | Issue | Kind | Waiting on |
|---|---|---|---|
| 1 | #250 | `type:research` — the `type:docs` half landed | **MVS time**, two `/.dm` calls |
| 1 | #256 | **regression in a published release** | PR #257 merged, then a **decision** — below |
| 2 | #254 | `type:docs` — six Parmlib keywords undocumented | nothing |
| 3 | #250 | `type:research` — the `type:docs` half landed | **MVS time**, two `/.dm` calls |
| — | #198 | hygiene, explicitly not a bug | **#250(b)**, then milestone 4.1.0 |
| — | #176 | security, the heaviest by a wide margin | **RAKF** — see *Deferred* |

**Nothing open waits on a decision any more,** and with #237 merged nothing
open is a code bug either. Nothing open blocks 4.0.0: both #250 and #198 carry
the 4.1.0 milestone, and #176 is parked on another organisation.

#250 is still ranked first even though it is milestoned out, and the two are not
in conflict. The milestone says it is not a 4.0.0 deliverable; the rank says it
is the next thing to do, because it is read-only, costs minutes, and #198's
second step cannot be estimated until it is answered.
#256 outranks everything because it is the only item that is wrong on a system
someone can install today, and because it is the only one whose fix is already
written. #176 stays last for the reason it always did, not because it is small.

#250 is still ranked above the two parked items even though it is milestoned
out, and the two are not in conflict. The milestone says it is not a 4.0.0
deliverable; the rank says it is the next thing to do once #256 is off the
table, because it is read-only, costs minutes, and #198's second step cannot be
estimated until it is answered.

**One decision is open, and it is #256's second half.** The code fix is written
(PR #257); what needs the maintainer is how 4.0.1 reaches a system that already
installed 4.0.0. `THTP400` is ACCEPTed there, so a 4.0.1 full-function SYSMOD
under the same FMID cannot simply be received over it — and it does not need to
be. The corrected member is in the **sample library, which is not an SMP
element** (the generated `inst.jcl` says so itself), so such a system needs one
PROCLIB member replaced and nothing from SMP at all. That makes 4.0.1 a fresh
install's package plus a one-line instruction for everyone else, but whether it
ships as a re-tag over a deleted `v4.0.0` or beside it is not a call this file
makes.

**The return-code work is finished.** #226 and #245 between them settled every
exit that could end a refused start `CC 0000`; nothing in that thread is open,
and `docs/messages.md` §*A refused start* is where the resulting contract is
written down.

**Nothing MVS-side blocks the 4.0.0 tag any more.** The release work was never
in the tracker — it is in [`smp-todo.md`](smp-todo.md) — and as of 2026-08-24 it
is done: `THTP400` is free in the CDS *and* the ACDS on both stands (O1), the
full install was rehearsed under the throwaway FMID `TTST400` and cleaned back
off with UCLIN, every step CC 0000 (O2), and the document root ships as a UFS
image (O3, #252). What is left there is a checksum for the release assets and
O4, a convenience sample nobody is blocked on.
**4.0.0 tagged and shipped on 2026-08-24, and is being replaced.** The release
work was never in the tracker — it is in [`smp-todo.md`](smp-todo.md) — and it
was done: `THTP400` free in the CDS *and* the ACDS on both stands (O1), the full
install rehearsed under the throwaway FMID `TTST400` and cleaned back off with
UCLIN, every step CC 0000 (O2), the document root shipping as a UFS image (O3,
#252). What is left there is a checksum for the release assets and O4, a
convenience sample nobody is blocked on.

What none of that rehearsal caught is #256: the install was verified to *work*,
never that the procedure it installed still allocated what the CGI it routes to
needs. The published body is preserved at
[`docs/release-notes/v4.0.0.md`](release-notes/v4.0.0.md) so the changelog
survives the replacement — the 4.0.1 notes are built from it.

---

### 1 · #250 — does a LINKed module survive in the Job Pack Area?
### 1 · #256 — the STC procedure stopped allocating what its modules open

*fix written (PR #257); what is left is a release decision, not code*

`c35ab59` took `HASPCKPT` and `HASPACE1` out of `samplib/httpd` because
`HTTPJES2` was gone and nothing in `httpd/src/` opened them. Both halves of that
were true and the conclusion was still wrong: a CGI module is dispatched by the
LINK SVC **into HTTPD's task**, so it opens every ddname against the STC's
allocations, and mvsMF's jobs API opens both data sets through libc370's
`jesopen()`. 4.0.0 therefore shipped a procedure that breaks the API its own
default configuration routes to.

The rank is not about size — the diff is two lines and some prose. It is that
this is the only open item that is wrong on a system someone can install today.

Two things worth keeping out of the issue thread:

- **`docs/migration.md` was the more severe half.** A missing DD breaks a *new*
install; that file told a *working* 3.3.x system to delete one it needs.
Damage that spreads by being read is worse than damage that sits in a package.
- **The lesson generalises past this bug.** Every "nothing uses this any more"
check in the 4.0.0 removals was scoped to this repo, and three other repos
ship modules that run inside this address space. Re-running it for the neighbouring removals in
`c35ab59` (`stck2tv`, `httpds_`, `HTTPJES2`, `HTTPDSL` across mvsmf, httplua,
httprexx) found nothing — so this was the one instance, not the pattern. The
rule is now in `CLAUDE.md`.

### 2 · #254 — six Parmlib keywords the parser accepts are undocumented

*nothing blocks it; it is small and nobody has picked it up*

Ranked above #250 only because it costs no MVS time. `httpprm.c` is the
authority for what the parser accepts; `docs/configuration.md` is what an
operator reads, and the two disagree by six keywords.

### 3 · #250 — does a LINKed module survive in the Job Pack Area?

*what is left of it needs a live server and nothing else*

Expand Down Expand Up @@ -216,6 +272,14 @@ Unblocked and outside this repo: `mvslovers/ftpd#97` (now the last Phase 1
item), `mvslovers/mvsmf#329`, `mvslovers/mvsmf#345`.
Also `blocked:rakf`: `mvslovers/ftpd#64`.

Filed out of #256 on 2026-08-25, neither blocking 4.0.1:
`mvslovers/mvsmf#357` states the DD requirement where a mvsMF installer will
look for it — httpd documents it from one side of the boundary, nothing
documented it from the other. `mvslovers/libc370#142` is the fix that removes
the class: `jesopen()` should dynalloc the checkpoint and spool the way
`jesiropn()` already dynallocs the INTRDR, which also gets the site-specific
`VOL=SER` out of every procedure in the ecosystem.

Closed *not planned* on 2026-08-23: `mvslovers/ufsd#65` — reasoning in its
closing comment and in `docs/identity-redesign.md` §3.2. The per-client
permission question that replaces it is `mvslovers/ufsd#67`.
42 changes: 42 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,48 @@ Comment the three lines out, or install mvsMF:
STC's STEPLIB concatenation — HTTPD dispatches a module through the MVS LINK
SVC, which searches the task's own libraries.

#### mvsMF's jobs API needs the JES2 DDs

The same mechanism decides more than where the module is found. A module runs
**in HTTPD's task**, so every ddname it opens has to be allocated by the STC
procedure — a CGI has no allocations of its own.

mvsMF's jobs API reads the JES2 checkpoint and spool directly, by DD name, and
the shipped procedure allocates them:

```
//HASPCKPT DD DISP=SHR,DSN=SYS1.HASPCKPT,UNIT=3350,VOL=SER=MVS000
//HASPACE1 DD DISP=SHR,DSN=SYS1.HASPACE,UNIT=3350,VOL=SER=SPOOL1
```

**Check `UNIT` and `VOL=SER` against your own JES2 procedure** before the first
start — they are site values, not constants, and the ones above are only what
the pattern has always carried.

Without the two DDs the server starts normally and everything else works. The
jobs API does not — and it does not fail uniformly:

| request | answer |
|---|---|
| job list (`GET /zosmf/restjobs/jobs`) | `500`, `REASON_INCORRECT_JES_VSAM_HANDLE` |
| spool retrieval | `500`, same reason |
| anything resolving one job by name and id | **`404` job not found** |

The 404 is the one to know about. `find_job_by_name_and_id()` returns NULL
whether JES2 is unreachable or the job genuinely is not there, and its callers
report the second. A client is told the job does not exist while it does — so
read the console, not the status code:

```
Unable to open checkpoint dataset DD:HASPCKPT
MVSMF201E UNABLE TO OPEN THE JES2 CHECKPOINT AND SPOOL DATA SETS
```

Job submit is unaffected; it dynallocs its own INTRDR.

If you serve no JES2 traffic at all, the DDs can go. Nothing else in HTTPD opens
them.

### RAKF is required for logins

HTTPD has no user database of its own. Every login — the HTML form, HTTP Basic,
Expand Down
6 changes: 5 additions & 1 deletion docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,11 @@ The 4-tier time-series statistics (`httpstat.c`, `httprepo.c`) with dataset pers

The built-in web interfaces for JES2 job browsing (`HTTPJES2`) and dataset listing (`HTTPDSL`) are no longer built or shipped. [mvsMF](https://github.com/mvslovers/mvsmf)'s dataset and jobs REST APIs replace them.

Two things to check when migrating a 3.3.x configuration: drop any `CGI=`/`MOD=` line naming `HTTPDSL` or `HTTPJES2` — the route is accepted but the program load fails on the first matching request — and drop the `HASPCKPT` and `HASPACE1` DDs from the STC procedure, which existed only so `HTTPJES2` could read the JES2 spool.
When migrating a 3.3.x configuration, drop any `CGI=`/`MOD=` line naming `HTTPDSL` or `HTTPJES2` — the route is accepted but the program load fails on the first matching request.

**Keep the `HASPCKPT` and `HASPACE1` DDs in the STC procedure.** `HTTPJES2` was not their only reader. A CGI module is dispatched by the MVS LINK SVC into HTTPD's own task, so it sees HTTPD's allocations — and mvsMF's jobs API opens both data sets by DD name, through libc370's `jesopen()`. Without them the job list and spool retrieval answer 500, and anything resolving a single job by name and id answers **404 job not found** — `find_job_by_name_and_id()` returns NULL for an unreachable JES2 exactly as it does for an absent job, so the status code misreports the cause. Every one of them writes two console lines per request; that is the reliable signal. Job submit still works — it dynallocs its own INTRDR.

Remove them only if no module in your server reaches JES2. 4.0.0 shipped a procedure without them — see [issue #256](https://github.com/mvslovers/httpd/issues/256).

The sources are kept under `tbd/` for reference and are outside the build.

Expand Down
39 changes: 39 additions & 0 deletions docs/release-notes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Release notes

The published GitHub Release body for each tag, kept here as the source of
record. GitHub is not one, for two separate reasons.

**The workflow overwrites the body.** `make release` pushes a tag, which runs
mbt's shared `release.yml`. That job **deletes every existing release for the
tag** and recreates it with `gh release create --generate-notes` — a body built
from commit subjects. A hand-written body is not an input to that process and
does not survive a re-push of the same tag.

**A deleted release takes its body with it.** `v4.0.0` shipped an STC procedure
without the JES2 DDs its own documented CGI needed
([#256](https://github.com/mvslovers/httpd/issues/256)), so it was replaced
rather than amended. [`v4.0.0.md`](v4.0.0.md) is that body, captured before the
deletion — the 4.0.1 notes are built from it so the changelog survives the
correction.

## How a body here reaches a release

It does not, on its own. After the release workflow finishes:

```sh
gh release edit v4.0.1 --repo mvslovers/httpd \
--notes-file docs/release-notes/v4.0.1.md
```

Order matters — run it after the workflow, or the recreate discards it.

## Writing the next one from the last

Reusing a body means substituting every version string in it, not just the
heading. `v4.0.0.md` carries `httpd-4.0.0-dist.zip` and five more artifact
names, the `HTTPD.V4R0M0.*` dataset qualifiers, and `V4R0M0` inside the sample
JCL it quotes. Diff the rendered file against the real `dist/` listing before
publishing.

One file per tag, named for it. Write it when the release is published, not
later.
Loading