Skip to content

Commit 838c7a2

Browse files
RamazanKaraclaude
andcommitted
Prepare PocketStack v1.1.0 release
Correctness, security, and honesty hardening on top of v1.0.3. Analyzer: - tolerate Compose port ranges (e.g. 3000-3005) instead of aborting - recognize registry-qualified and Docker Hub library/ image names - skip profile-gated services (mirrors a default compose up), with a warning - report extends: as an explicit, actionable blocker Runtime / generated demos: - escape untrusted service names and OpenAPI route data before innerHTML (XSS) - pin CDN runtime packages (WebContainer, PGlite) so demos do not break on upstream majors - surface honest failures for missing COOP/COEP, file:// hosting, and CDN load errors - fix WASI fd_write UTF-8 decoding across iovec boundaries - run multi-statement PGlite queries via exec, matching the SQLite adapter - sandbox the Studio uploaded-HTML preview iframe; aria-live status/logs CLI / tooling / docs: - print project-level warnings in human analyze output; add CLI tests - resilient GitHub Pages build (skip missing demos/media) - add CHANGELOG, v1.1.0 release notes, Compose Features docs - harden SECURITY.md (supported versions, reporting channel) - remove dead code; add regression tests for every fix Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 3ad3911 commit 838c7a2

24 files changed

Lines changed: 714 additions & 125 deletions

CHANGELOG.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Changelog
2+
3+
All notable changes to PocketStack are documented here. The format is based on
4+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project
5+
follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6+
7+
Per-release detail lives in [docs/RELEASE_NOTES_v*.md](docs/).
8+
9+
## [1.1.0] - 2026-06-28
10+
11+
### Added
12+
- `extends:` services are reported as an explicit, actionable blocker with a
13+
suggestion to flatten the service.
14+
- `pocketstack analyze` prints project-level warnings (COOP/COEP, network
15+
access, skipped profile services) in its default human-readable output.
16+
- "Compose Features" documentation; `SECURITY.md` supported-versions table and
17+
private vulnerability reporting channel.
18+
- CLI tests and regression tests for port ranges, image normalization, profile
19+
skipping, `extends`, and the WASI UTF-8 fix.
20+
21+
### Changed
22+
- Services gated behind `profiles:` are skipped to match a default
23+
`docker compose up`; they no longer count toward or block readiness.
24+
- Browser runtime packages (WebContainer, PGlite, sql.js, Wasmer) loaded from
25+
public CDNs are version-pinned.
26+
- The GitHub Pages build skips missing demos/media with a warning instead of
27+
failing.
28+
29+
### Fixed
30+
- Port ranges such as `3000-3005:3000-3005` no longer abort analysis.
31+
- Registry-qualified and Docker Hub official image names
32+
(`docker.io/library/postgres:16`, `library/postgres`, `ghcr.io/org/app`) now
33+
resolve to the correct adapter.
34+
- WASI `fd_write` decodes UTF-8 sequences split across iovec boundaries instead
35+
of corrupting them.
36+
- The PGlite query panel and bridge use `exec` so multi-statement SQL
37+
(`insert …; select …;`) runs, matching the SQLite adapter.
38+
39+
### Security
40+
- The generated demo dashboard escapes service names and OpenAPI route data
41+
before rendering, preventing HTML/script injection from untrusted input.
42+
- PocketStack Studio sandboxes its uploaded-HTML preview iframe.
43+
- Frontend, mock-http, database, and WASI demos now surface honest failure
44+
messages for missing cross-origin isolation, `file://` hosting, and CDN load
45+
failures.
46+
47+
## [1.0.3] - 2026-05-27
48+
Refocused the product around browser-native readiness: `analyze` reports a
49+
readiness score, blockers, suggestions, and next steps; clearer primary
50+
unsupported reasons; conversion guide. See
51+
[release notes](docs/RELEASE_NOTES_v1.0.3.md).
52+
53+
## [1.0.2] - 2026-05-27
54+
Hosted Studio, generated example demos, and the GitHub Pages site. See
55+
[release notes](docs/RELEASE_NOTES_v1.0.2.md).
56+
57+
## [1.0.1] - 2026-05-26
58+
Hardening release. See [release notes](docs/RELEASE_NOTES_v1.0.1.md).
59+
60+
## [1.0.0] - 2026-05-26
61+
Initial browser-native PocketStack release.
62+
63+
[1.1.0]: https://github.com/ramazankara/pocketstack/releases/tag/v1.1.0
64+
[1.0.3]: https://github.com/ramazankara/pocketstack/releases/tag/v1.0.3
65+
[1.0.2]: https://github.com/ramazankara/pocketstack/releases/tag/v1.0.2
66+
[1.0.1]: https://github.com/ramazankara/pocketstack/releases/tag/v1.0.1
67+
[1.0.0]: https://github.com/ramazankara/pocketstack/releases/tag/v1.0.0

CODE_OF_CONDUCT.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ Be respectful, concrete, and kind.
44

55
PocketStack is an open-source developer tool. Contributions should help people
66
understand the browser-only boundary without dismissing their use cases.
7+
8+
Report unacceptable behavior privately to the maintainers through the GitHub
9+
repository's contact channels. Reports are handled confidentially.

CONTRIBUTING.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ go vet ./...
2323
make smoke
2424
```
2525

26+
`make smoke` builds the binary, regenerates every example demo, and runs the
27+
generated-demo checks. `make release-check` additionally runs `go vet`, a
28+
GoReleaser snapshot build, and checksum verification — run it before preparing a
29+
release.
30+
31+
Work on a branch and open a pull request against `main`. Note user-facing
32+
changes in [CHANGELOG.md](CHANGELOG.md) under an `Unreleased`/next-version
33+
heading; the [release process](docs/RELEASE.md) covers tagging and publishing.
34+
2635
New adapters should add:
2736

2837
- analyzer classification tests;

SECURITY.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,37 @@ include a PocketStack backend, runner, or Docker daemon integration.
1010
- Generated demos run in the viewer's browser and may use browser storage such
1111
as IndexedDB.
1212
- Frontend/WebContainer demos and database/WASI adapters may load public
13-
browser runtime packages from the network.
13+
browser runtime packages from version-pinned public CDNs (esm.sh, jsDelivr,
14+
unpkg, cdnjs). These are pinned to a major/known version but are not currently
15+
served with Subresource Integrity, so a CDN compromise would affect demos that
16+
load that adapter. Vendor the runtime assets if you need a hardened offline
17+
build.
1418
- Unsupported container features remain unsupported instead of being emulated
1519
unsafely.
1620

1721
Generated demos can expose whatever files you package into them. Review copied
1822
assets, fixtures, SQL seeds, environment values, and frontend source before
19-
publishing a demo to a public host.
23+
publishing a demo to a public host. Service names and OpenAPI route paths are
24+
escaped before they are rendered in the demo dashboard.
25+
26+
## Supported Versions
27+
28+
| Version | Supported |
29+
| ------- | --------- |
30+
| 1.1.x ||
31+
| < 1.1 ||
32+
33+
Security fixes land on the latest minor release. Upgrade to the newest release
34+
before reporting an issue.
2035

2136
## Reporting
2237

23-
Report security issues privately through the GitHub repository.
38+
Please report security issues privately using GitHub's private vulnerability
39+
reporting at
40+
<https://github.com/ramazankara/pocketstack/security/advisories/new>
41+
(repository **Security** tab → **Report a vulnerability**). Do not open a public
42+
issue for a suspected vulnerability.
43+
44+
You can expect an acknowledgement within a few days. Please include reproduction
45+
steps and the affected version, and allow a reasonable window for a fix before
46+
any public disclosure.

docs/ANNOUNCEMENT_LINKEDIN.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Think:
4343
- WASI demos
4444
- open-source projects that need a first-click experience
4545

46-
PocketStack v1.0.2 is live now.
46+
PocketStack v1.1.0 is live now.
4747

4848
It includes a hosted Studio, generated demo examples, release binaries, and a
4949
short live demo video.
@@ -55,7 +55,7 @@ GitHub:
5555
https://github.com/RamazanKara/pocketstack
5656

5757
Release:
58-
https://github.com/RamazanKara/pocketstack/releases/tag/v1.0.2
58+
https://github.com/RamazanKara/pocketstack/releases/tag/v1.1.0
5959

6060
This is early, but it already feels like the right direction:
6161

@@ -85,7 +85,7 @@ The niche is smaller and sharper:
8585

8686
if your stack is demo-shaped, PocketStack gives you a link people can click.
8787

88-
v1.0.2 is live now with hosted Studio, example demos, and release binaries.
88+
v1.1.0 is live now with hosted Studio, example demos, and release binaries.
8989

9090
Try the hosted Studio:
9191
https://ramazankara.github.io/pocketstack/

docs/COMPATIBILITY.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,28 @@ The short version:
4040
Unsupported does not mean impossible forever. It means there is no honest
4141
browser adapter for that behavior yet.
4242

43+
## Compose Features
44+
45+
PocketStack analyzes a single Compose file and maps the default service set to
46+
browser adapters. A few Compose directives are handled specifically:
47+
48+
- `profiles:` — services gated behind a profile are not started by a default
49+
`docker compose up`, so PocketStack skips them. They do not count toward, or
50+
block, browser readiness, and the analysis warns when services are skipped.
51+
- `extends:` — not supported. PocketStack does not resolve an extended base
52+
service. Flatten the service (inline its image, labels, ports, and volumes)
53+
before analyzing.
54+
- Multiple Compose files / overrides — pass a single file with `-f`. Merge any
55+
overrides yourself first.
56+
- `depends_on:` and `healthcheck:` — parsed but ignored. A static demo has no
57+
startup ordering or health gating.
58+
- Port ranges such as `3000-3005:3000-3005` are accepted; the first port of the
59+
range is used and adapter selection does not depend on the exact port.
60+
- Image references are normalized, so short names (`postgres`), Docker Hub
61+
official names (`library/postgres`), and registry-qualified names
62+
(`docker.io/library/postgres:16`, `ghcr.io/org/app`) resolve to the same
63+
adapter.
64+
4365
## Readiness Report
4466

4567
Every analysis result includes:

docs/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ Then go deeper:
2424
for the public site and generated demos.
2525
- [Release process](RELEASE.md): local checks, GoReleaser, checksums, and tag
2626
publishing.
27-
- [v1.0.3 release notes](RELEASE_NOTES_v1.0.3.md): current release highlights
27+
- [Changelog](../CHANGELOG.md): version history at a glance.
28+
- [v1.1.0 release notes](RELEASE_NOTES_v1.1.0.md): current release highlights
2829
and constraints.
2930

3031
PocketStack v1 is browser-only. Unsupported Compose features should remain

docs/RELEASE_NOTES_v1.1.0.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# PocketStack v1.1.0
2+
3+
PocketStack v1.1.0 is a correctness, honesty, and hardening release. The
4+
browser-native product boundary is unchanged; this release makes the analyzer
5+
handle more real-world Compose files correctly and makes generated demos fail
6+
honestly instead of silently.
7+
8+
## Analyzer fixes
9+
10+
- Port ranges such as `3000-3005:3000-3005` no longer abort analysis. The first
11+
port of the range is used.
12+
- Registry-qualified and Docker Hub official image names now resolve to the
13+
right adapter. `docker.io/library/postgres:16` and `library/postgres` are
14+
recognized the same as `postgres`; registry hosts like `ghcr.io/...` are
15+
normalized away while plain namespaces (`nginxinc/...`) are preserved.
16+
- Services gated behind `profiles:` are skipped, matching what a default
17+
`docker compose up` starts. They no longer count toward or block browser
18+
readiness, and the analysis warns when services are skipped.
19+
- `extends:` is now reported as an explicit, actionable blocker instead of a
20+
confusing per-field rejection, with a suggestion to flatten the service.
21+
- `pocketstack analyze` now prints project-level warnings (COOP/COEP, network
22+
access, skipped profile services) in its default human-readable output, not
23+
only in `--json`.
24+
25+
## Generated demo / runtime
26+
27+
- Security: the demo dashboard escapes service names and OpenAPI route data
28+
before rendering, preventing HTML/script injection from untrusted Compose or
29+
spec input. PocketStack Studio sandboxes its uploaded-HTML preview iframe.
30+
- Honest failures: frontend demos now fail fast with a clear COOP/COEP message
31+
when the host is not cross-origin isolated; mock-http and database demos
32+
explain that they need http(s) hosting rather than `file://`; and CDN load
33+
failures report which package and URL could not be reached.
34+
- Browser runtime packages loaded from public CDNs (WebContainer, PGlite, sql.js,
35+
Wasmer) are version-pinned so previously generated demos do not break when an
36+
upstream package ships a new major.
37+
- WASI `fd_write` now concatenates iovecs before decoding, fixing corruption of
38+
multi-byte UTF-8 output split across buffer boundaries.
39+
- The PGlite query panel and demo query bridge now run multi-statement SQL via
40+
`exec`, matching the SQLite adapter so the same `insert …; select …;` input
41+
works in both.
42+
43+
## Tooling & docs
44+
45+
- The GitHub Pages build skips missing demos and media with a warning instead of
46+
failing the whole build when run outside `make pages`.
47+
- New "Compose Features" documentation describes how `profiles`, `extends`,
48+
port ranges, image normalization, and orchestration directives are handled.
49+
- `SECURITY.md` now lists supported versions and a private vulnerability
50+
reporting channel.
51+
- Added CLI tests plus regression tests for port ranges, image normalization,
52+
profile skipping, `extends`, and the WASI UTF-8 fix.
53+
54+
## Known constraints
55+
56+
- PocketStack remains browser-native only. It does not run arbitrary Docker
57+
containers, Docker builds, Linux daemons, or real container networking in the
58+
browser.
59+
- `extends` and multi-file Compose overrides are not resolved; flatten or merge
60+
before analyzing.
61+
- Unsupported services should be converted to static assets, frontend projects,
62+
WASI modules, OpenAPI mocks, SQLite, PGlite, or in-browser state when that is
63+
honest for the demo.

internal/cli/cli.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ func analyze(args []string, stdout, stderr io.Writer) int {
8181
fmt.Fprintf(stdout, " suggestion: %s\n", suggestion)
8282
}
8383
}
84+
if len(analysis.Warnings) > 0 {
85+
fmt.Fprintln(stdout, "\nWarnings:")
86+
for _, warning := range analysis.Warnings {
87+
fmt.Fprintf(stdout, " - %s\n", warning)
88+
}
89+
}
8490
if !analysis.BrowserNative {
8591
fmt.Fprintln(stdout, "\nNext steps:")
8692
for _, step := range analysis.NextSteps {

internal/cli/cli_test.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package cli
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
"testing"
10+
)
11+
12+
func writeStaticProject(t *testing.T) string {
13+
t.Helper()
14+
root := t.TempDir()
15+
if err := os.MkdirAll(filepath.Join(root, "site"), 0o755); err != nil {
16+
t.Fatal(err)
17+
}
18+
if err := os.WriteFile(filepath.Join(root, "site", "index.html"), []byte("<h1>hi</h1>"), 0o644); err != nil {
19+
t.Fatal(err)
20+
}
21+
composeFile := filepath.Join(root, "compose.yaml")
22+
if err := os.WriteFile(composeFile, []byte(`
23+
services:
24+
web:
25+
image: nginx:alpine
26+
volumes:
27+
- ./site:/usr/share/nginx/html:ro
28+
debugtool:
29+
image: redis:7
30+
profiles:
31+
- debug
32+
`), 0o644); err != nil {
33+
t.Fatal(err)
34+
}
35+
return composeFile
36+
}
37+
38+
func TestRunAnalyzeHumanOutputSurfacesProfileWarning(t *testing.T) {
39+
composeFile := writeStaticProject(t)
40+
var stdout, stderr bytes.Buffer
41+
code := Run([]string{"analyze", "-f", composeFile}, &stdout, &stderr)
42+
if code != 0 {
43+
t.Fatalf("exit code = %d, stderr = %q", code, stderr.String())
44+
}
45+
out := stdout.String()
46+
for _, want := range []string{"Browser readiness: 100%", "web: static-web adapter", "Warnings:", "profile-gated"} {
47+
if !strings.Contains(out, want) {
48+
t.Fatalf("analyze output missing %q:\n%s", want, out)
49+
}
50+
}
51+
// The service is named in the skip warning, but must not appear as an
52+
// analyzed service line (" debugtool: ...").
53+
if strings.Contains(out, " debugtool:") {
54+
t.Fatalf("profile-gated service should not be listed as analyzed:\n%s", out)
55+
}
56+
}
57+
58+
func TestRunAnalyzeJSONIsValid(t *testing.T) {
59+
composeFile := writeStaticProject(t)
60+
var stdout, stderr bytes.Buffer
61+
code := Run([]string{"analyze", "-f", composeFile, "--json"}, &stdout, &stderr)
62+
if code != 0 {
63+
t.Fatalf("exit code = %d, stderr = %q", code, stderr.String())
64+
}
65+
var payload struct {
66+
Mode string `json:"mode"`
67+
Readiness struct {
68+
Score int `json:"score"`
69+
} `json:"readiness"`
70+
Warnings []string `json:"warnings"`
71+
}
72+
if err := json.Unmarshal(stdout.Bytes(), &payload); err != nil {
73+
t.Fatalf("analyze --json did not produce valid JSON: %v\n%s", err, stdout.String())
74+
}
75+
if payload.Mode != "browser-native" || payload.Readiness.Score != 100 {
76+
t.Fatalf("unexpected analysis payload: %+v", payload)
77+
}
78+
if len(payload.Warnings) == 0 {
79+
t.Fatalf("expected a profile-skip warning in JSON output")
80+
}
81+
}
82+
83+
func TestRunAnalyzeMissingFileFails(t *testing.T) {
84+
var stdout, stderr bytes.Buffer
85+
code := Run([]string{"analyze", "-f", filepath.Join(t.TempDir(), "nope.yaml")}, &stdout, &stderr)
86+
if code != 1 {
87+
t.Fatalf("exit code = %d, want 1", code)
88+
}
89+
if stderr.Len() == 0 {
90+
t.Fatalf("expected an error message on stderr")
91+
}
92+
}
93+
94+
func TestRunDemoGeneratesOutput(t *testing.T) {
95+
composeFile := writeStaticProject(t)
96+
outDir := filepath.Join(t.TempDir(), "demo")
97+
var stdout, stderr bytes.Buffer
98+
code := Run([]string{"demo", "-f", composeFile, "-o", outDir}, &stdout, &stderr)
99+
if code != 0 {
100+
t.Fatalf("exit code = %d, stderr = %q", code, stderr.String())
101+
}
102+
if _, err := os.Stat(filepath.Join(outDir, "pocketstack.manifest.json")); err != nil {
103+
t.Fatalf("demo did not write a manifest: %v", err)
104+
}
105+
}
106+
107+
func TestRunVersionAndUnknownCommand(t *testing.T) {
108+
var stdout, stderr bytes.Buffer
109+
if code := Run([]string{"version"}, &stdout, &stderr); code != 0 {
110+
t.Fatalf("version exit code = %d", code)
111+
}
112+
if strings.TrimSpace(stdout.String()) == "" {
113+
t.Fatalf("version produced no output")
114+
}
115+
116+
stdout.Reset()
117+
stderr.Reset()
118+
if code := Run([]string{"frobnicate"}, &stdout, &stderr); code != 2 {
119+
t.Fatalf("unknown command exit code = %d, want 2", code)
120+
}
121+
}

0 commit comments

Comments
 (0)