refactor: keep compiler source standalone
Signed-off-by: Cole Speelman <crspeelman@gmail.com>
This commit is contained in:
+2
-1
@@ -13,13 +13,14 @@ Sandwich Hime follows semantic versioning after v1. Compiler and nested runtime
|
||||
- Read-only stale-output checking and structured diagnostics.
|
||||
- Independent Apache-2.0 `sando` component/runtime ABI.
|
||||
- Loopback-only last-good development supervisor with SSE reload and diagnostic overlay.
|
||||
- Synthetic EQL-shaped proof fixture and production pilot plan.
|
||||
- Compiler-owned deterministic golden fixture and standalone release gates.
|
||||
- Multi-license, security, governance, trademark, AI contribution, and release policies.
|
||||
|
||||
### Removed
|
||||
|
||||
- Unpublished `.go.hime` syntax and 2025 generated API.
|
||||
- Injected helper directories, nested demo modules, Go plugins, and manually repaired generated output.
|
||||
- Repository-bundled application examples and deployment-specific evidence.
|
||||
- Placeholder novelty commands that did not perform project work.
|
||||
|
||||
Private prototype history is intentionally outside the sanitized public repository. The public changelog begins with the pre-1.0 source preview.
|
||||
|
||||
+7
-3
@@ -2,7 +2,11 @@
|
||||
|
||||
# Contributing
|
||||
|
||||
The canonical public project and only contribution venue is the Gamertan Gitea repository. Repository maintainers may temporarily disable issue or patch intake during the pre-1.0 preview; do not route around a closed intake channel by sending unsolicited private patches.
|
||||
The canonical public project and only contribution venue is the
|
||||
[founder-controlled Gitea repository](https://gitea.speelman.ca/gamertan/sandwich-hime).
|
||||
Repository maintainers may temporarily disable issue or patch intake during the
|
||||
pre-1.0 preview; do not route around a closed intake channel by sending
|
||||
unsolicited private patches.
|
||||
|
||||
Public pre-1.0 contributions use Developer Certificate of Origin 1.1 sign-off. The proposed `CLA.md` is an inactive draft, is not a condition of contribution, and creates no contributor or project obligations. If a contribution agreement is ever activated after legal review, the project will announce its prospective terms rather than silently applying the draft.
|
||||
|
||||
@@ -13,10 +17,10 @@ go test ./...
|
||||
go test -race ./...
|
||||
go vet ./...
|
||||
(cd sando && go test -race ./... && go vet ./...)
|
||||
go run ./cmd/himesan check ./examples/eql-shaped
|
||||
./scripts/check-licenses.sh
|
||||
```
|
||||
|
||||
Changes require focused tests, stable diagnostics, formatted generated goldens when applicable, documentation for public behavior, and a signed-off commit (`git commit -s`). The sign-off certifies the [DCO](DCO.txt); it is not a copyright assignment or acceptance of the inactive CLA. Do not commit an EQL database, production data, secrets, build candidates, or developer cache files.
|
||||
Changes require focused tests, stable diagnostics, formatted generated goldens when applicable, documentation for public behavior, and a signed-off commit (`git commit -s`). The sign-off certifies the [DCO](DCO.txt); it is not a copyright assignment or acceptance of the inactive CLA. Do not commit production data, private application fixtures, secrets, build candidates, or developer cache files.
|
||||
|
||||
The project requires no copyright assignment. Ownership remains determined by applicable law and any employer or other agreement. Contributors submit each file under the license identified for that repository area, and the DCO records their certification that they have the right to do so. Material AI assistance must follow [AI_CONTRIBUTIONS.md](AI_CONTRIBUTIONS.md). Review considers provenance, safety, maintenance cost, compatibility, and fit—not just whether code passes tests.
|
||||
|
||||
|
||||
+10
-5
@@ -7,10 +7,9 @@ Sandwich Hime deliberately separates the development tool from application runti
|
||||
|
||||
| Path or material | License |
|
||||
| --- | --- |
|
||||
| Project-authored files in the repository root, `cmd/**`, `internal/**`, `docs/**`, `scripts/**`, and `site/**`, except the legal texts listed below | AGPL-3.0-only |
|
||||
| Project-authored files in the repository root, `cmd/**`, `internal/**`, `docs/**`, and `scripts/**`, except the legal texts listed below | AGPL-3.0-only |
|
||||
| Nested `sando/**` runtime module, except its verbatim license text | Apache-2.0 |
|
||||
| Copyable `examples/**`, except their generated-file treatment described below | 0BSD |
|
||||
| `LICENSE`, `sando/LICENSE`, example `LICENSE` files, and `DCO.txt` | Their own stated copying terms and notices |
|
||||
| `LICENSE`, `sando/LICENSE`, and `DCO.txt` | Their own stated copying terms and notices |
|
||||
| User-authored `.sando` templates | Chosen by their author, subject to rights in their inputs |
|
||||
| Generated application `.sando.go` files | Chosen by the template/application author, subject to rights in their inputs and dependencies |
|
||||
|
||||
@@ -18,13 +17,19 @@ Sandwich Hime claims no copyright in a user's template merely because the compil
|
||||
|
||||
The AGPL compiler is a separately installed development process. The Apache runtime must never import an AGPL package. Importing the Apache runtime or using generated output does not, by itself, incorporate the compiler into an application. Redistribution of the runtime remains subject to Apache-2.0 and any other applicable third-party obligations.
|
||||
|
||||
`COPYRIGHT` identifies Cole Speelman's original project work without claiming contributor-owned work. The nested runtime carries its own `sando/COPYRIGHT`, and the example identifies its original author in `examples/eql-shaped/LICENSE`. The project requires no copyright assignment; ownership of contributions remains determined by applicable law and existing agreements.
|
||||
The `.sando` source and committed `.sando.go` output under
|
||||
`internal/compiler/testdata/golden/**` are non-copyable compiler test fixtures
|
||||
covered by the AGPL `internal/**` map above. The generated fixture intentionally
|
||||
has no inline SPDX header because that absence is one of the generator's tested
|
||||
application-output properties.
|
||||
|
||||
`COPYRIGHT` identifies Cole Speelman's original project work without claiming contributor-owned work. The nested runtime carries its own `sando/COPYRIGHT`. The project requires no copyright assignment; ownership of contributions remains determined by applicable law and existing agreements.
|
||||
|
||||
A compiler contribution that adds text intended to be copied into generated output must record the `Himesan-Output-Permission: v1.0` grant required by [CONTRIBUTING.md](CONTRIBUTING.md). Without that grant, the contribution must be designed so its contributor-owned text is not emitted. DCO sign-off alone does not grant the additional output permission.
|
||||
|
||||
Official flags, mascots, and badges are not covered merely because they use a project mark. Each published asset must identify its copyright holder and reuse license.
|
||||
|
||||
SPDX identifiers state the applicable license for comment-capable source and documentation. Directory-level maps cover generated files and formats such as JSON that cannot safely carry comments. Full license texts are at `LICENSE` and `sando/LICENSE`; examples carry their own `LICENSE` and `LICENSES.md`. License texts and the verbatim `DCO.txt` retain their own notices and are not relicensed as project documentation.
|
||||
SPDX identifiers state the applicable license for comment-capable source and documentation. Directory-level maps cover generated files and formats such as JSON that cannot safely carry comments. Full license texts are at `LICENSE` and `sando/LICENSE`. Copyable examples are maintained in separate repositories and must declare their own licenses; official copyable examples are intended to use 0BSD. License texts and the verbatim `DCO.txt` retain their own notices and are not relicensed as project documentation.
|
||||
|
||||
The snapshot exporter's `PUBLIC-SNAPSHOT.json` and `PUBLIC-SNAPSHOT.sha256` are generated factual provenance records and intentionally carry no inline SPDX comment. They do not change the license of any listed file.
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"schema_version":2,"project":"sandwich-hime","export_policy":"exact-allowlist-v1","export_mode":"release","file_count":98,"allowlist_sha256":"91213f72ddef67aaed7764f08f89fd13281bb79855f3680e1ec0d05cda0e133d","manifest_sha256":"e2eec9fed86828dff900a09f39a05021c48f295bf87a1a9b2896d673886c9a69"}
|
||||
{"schema_version":2,"project":"sandwich-hime","export_policy":"exact-allowlist-v1","export_mode":"release","file_count":77,"allowlist_sha256":"db978285858ba5a1fefeb732d716338d8c652c5fc583f465d08f80b9ec74f0a9","manifest_sha256":"6bc38ff6e8ee243f840dbd768769f5be718f5fd01ba26855038fa15c5e518cc9"}
|
||||
|
||||
+16
-37
@@ -2,53 +2,35 @@
|
||||
658ba4b4645426f8c3249337f47669074ae9249a31703dcd9ea4c1afec45e20b ./.gitattributes
|
||||
d5ae411fb422b2388cac220f9655900eecbc49ece961b2bb2a6610347733b756 ./.gitignore
|
||||
98f663ab0f376b4550094465ec2e06192d1e0b0707604ec6794f20b0d10952c1 ./AI_CONTRIBUTIONS.md
|
||||
cf2e75a6e087570064dc3d8d8ed313676c4e8df1d422bf483d33d81751174bdc ./CHANGELOG.md
|
||||
7f4414fed21e9578b8d1a0d109ba807cb79d28a9290d58df49cc75145d9d1068 ./CHANGELOG.md
|
||||
b696cab3cf482ff5737501371cca749369b119351383e698ced42bcdbcbfc8ae ./CLA.md
|
||||
af0a256657e192641b3d7cee24ece36a76287baf225f2a045da16327214ac7ec ./CONTRIBUTING.md
|
||||
2d98826969cd492cc4dd10c595be21a608def5aacbec40ea5e25f1c3a617d8e6 ./CONTRIBUTING.md
|
||||
86d7e49d5d90e0f98a4ad0f14b5d8b9f11ed09a1e29ecdf27388316b28e195e8 ./COPYRIGHT
|
||||
f7ac75b443f4ca16b503241344b41aeff9503b0c30bedc2b119551d83cb0fa90 ./DCO.txt
|
||||
73cb1b092a40c56e522c5a0ebddf2b44f347cdb57bf6994cdb305d0e5697b55e ./GENERATED_CODE.md
|
||||
048f96acecd3af84d6abc4f8c38108dff2171445417e5132318a1d18de4279fe ./GOVERNANCE.md
|
||||
0d96a4ff68ad6d4b6f1f30f713b18d5184912ba8dd389f86aa7710db079abcb0 ./LICENSE
|
||||
1b398f6e2679a4c1d6c5a03468f66b86be34e82ed40e3a029935c74742502e7a ./LICENSES.md
|
||||
6587e439cb3c39dce9aaa5b51facdbd78c0e86c24e9e3fb9537b944435a74410 ./LICENSES.md
|
||||
b6aa08e5ccaec3c5dccdc19d7cd7f54a70adae4d57966263c7aa353c7ba70e08 ./MAINTAINERS.md
|
||||
6638db2f1fba831c79de835ce95c847a5b36c5b5c693b99a28655b2d096cc440 ./OUTPUT_EXCEPTION.md
|
||||
88150b7ae8759b62829f3a0ba2b03b1c44abc2f688349d5b3ad39ebf654a29d4 ./README.md
|
||||
6007b80cd80f8f238a1e77de6417aa0c69c159dce6f5a5bd085ef87b8865f5c9 ./RELEASE.md
|
||||
f5fe4d5bf5f8b72ad974060faf458ad26bb4b2c05c2bd9539533247b7462f76b ./ROADMAP.md
|
||||
531b3c183892c71974e25f790e8570ba72519f9299aab4031d23b62952a7682c ./README.md
|
||||
00ac24b5bc5cd2b62f6176f85d4be29087c59c17f4f670edacf408d56ccc7f23 ./RELEASE.md
|
||||
370c948b267527fdc2309c808b99089da33ac81346043dd54692ebb0843f6c0a ./ROADMAP.md
|
||||
2c86f5b983dfeb97a02d46850fa42e18cab1ed23201822aa3c344b9d2e1b0c3f ./SECURITY.md
|
||||
53bd6eda804d6b782bdb07115ec197c890813cf2d5d0125dfe8f47f5f92f75b0 ./SPEC.md
|
||||
842beff8afa72d120fcad0ac73afb2049d580ff3000975f3b1786c4ade6a14d4 ./TRADEMARKS.md
|
||||
136a6d82db842547b342f8b0c9ffdc7c04f7c9b473b4ef6dca9dbc940cb24b54 ./cmd/himesan/main.go
|
||||
3011a435aaeb572d34e2d2b582865fd63268c92a9ae3f0ae1a56d1a59d83cd43 ./cmd/himesan/main_test.go
|
||||
1ecbba46f8b1b2d548a01d7e98afae17b2dd17a814338ff1f88db885655d1c07 ./docs/ARCHITECTURE.md
|
||||
318ffcf2463036cd533b1c694f1f4840ef4b0c7c15bc049ec714bbcf4850ba15 ./docs/BENCHMARKS.md
|
||||
9c598559a89fa4a9bdd2311bd1ed8330992d0a0f74ec8b29ac151fc0ff8fef16 ./docs/BENCHMARKS.md
|
||||
5c3a62fed80ca28d56558b8c75e8b5be8ba7d2554127adf4609d96da314e85b0 ./docs/BRAND.md
|
||||
35f5b4b7d195a7b5c071d4665505afef189c7b386d4e3079e9ce8a96ace07f3a ./docs/COMPATIBILITY.md
|
||||
e4021b554ebc479954321586012add57a5fbfb58a1f7fce001d5638880912fc6 ./docs/DEVELOPMENT_SERVER.md
|
||||
51aa57a81131b64f76c45552122de842f22be92d81c8bba8f6fd38a18a7670d6 ./docs/DIAGNOSTICS.md
|
||||
f1a8e78c5aa521324ad2fcb386512158d0c0f9956e97f9a1bc8f97aa5d5e9844 ./docs/THREAT_MODEL.md
|
||||
7e2406acc98391ec126b13d512c00b930bbc2c19f4d5b0fe52286ae41bfbc92d ./examples/eql-shaped/LICENSE
|
||||
eaf381627e6020d7b87b3058ee9c2882bcca5280a8bd409547ecc955a33346db ./examples/eql-shaped/LICENSES.md
|
||||
78c58c00b93f8029cee67d8facdc81a30cc69feb7683952d1e72b54086485eb0 ./examples/eql-shaped/README.md
|
||||
2decbbebb78da922d39d0b74ad8358601526c1d36494c857d66d69116403b7ca ./examples/eql-shaped/cmd/example/main.go
|
||||
385bf9bd6a301723d123c2a52667d320889c47495f018fe0e7daa520c9fa89b0 ./examples/eql-shaped/go.mod
|
||||
6b53c55daf538b25fba2a60be9887fce086b8635a38120ab3f5bf6e9fcb67867 ./examples/eql-shaped/himesan.json
|
||||
2c0063ed8724520f146d6717bb7d828ce69be17b41cbfb8f4866664ba09a58a8 ./examples/eql-shaped/views/badge.sando
|
||||
6befc3b1bd216bb41c7da96e0e2f1c851f5531d2c6ac800f3d06b11de01c2878 ./examples/eql-shaped/views/badge.sando.go
|
||||
c5b402971618747c906c84508d5af9fd00aa33391b9b59f093b001a3cb0b2164 ./examples/eql-shaped/views/browse_results.sando
|
||||
065a88f19de5ba15fb7f9d2104e2a74fe2830f95b16c432e27e40f8aba000df9 ./examples/eql-shaped/views/browse_results.sando.go
|
||||
696c391538cdc50c5ab1abea23b561fa354e56eaf73b7ad6aabe130389715a8c ./examples/eql-shaped/views/home.sando
|
||||
cdfe566844319accf7a9eaae4b5b43ea32b7a0cb3d4fec2dad086d98fb3e9d0b ./examples/eql-shaped/views/home.sando.go
|
||||
c9a8102588cb94f946a36414291d12cb681e605acc93c2b5dc8d74fd33ae5603 ./examples/eql-shaped/views/layout.sando
|
||||
fea981d626bd696068b2384707a217527ad5e24e909d2358cc95444145c424fd ./examples/eql-shaped/views/layout.sando.go
|
||||
120fd733237ef1f15ce60b6ea27c21ce509f6ddc1a9727a9375bf2bb22b3629b ./examples/eql-shaped/views/model.go
|
||||
a8e647d7a9cff4804f97e2175c760cf04be6580a85a5eb946b733b46111b2e06 ./examples/eql-shaped/views/render_test.go
|
||||
8d59b45799d0ed2e9e6997f286315f2cb794ea660940978ec39efa3b9a3b94ab ./examples/eql-shaped/views/security_probe.sando
|
||||
9d5144c39442e29aded227828099cb4b1a32a69112559f8682b43fa04cfaae65 ./examples/eql-shaped/views/security_probe.sando.go
|
||||
f27c46ca63707bb8cc570eab1ea521824e94bc59b1d153998a5e91c2c7340d16 ./go.mod
|
||||
07d161772e9c6eec0dcc12179286e5c686dcabdc4e56a7cb8d112f640b072563 ./internal/compiler/backend.go
|
||||
0cb82706496f80e2a852917c047fb1f29138144c96323a23ba2565055aadcf03 ./internal/compiler/compiler_test.go
|
||||
28123757d27298dd81cf13ebd9242b24556734a35e36c2ac731f2a8475d70d28 ./internal/compiler/compiler_test.go
|
||||
d99ba263bf501ca81ed38ba88216c063d2fc22f4b45a3f28d5105957f449c4de ./internal/compiler/context.go
|
||||
b2a96ef1ad572ad9cd0e9247328ca261de6f9f3689da41e3f3e111d405a6dee6 ./internal/compiler/diagnostic.go
|
||||
42ccf512381e130bf593b065dccd7697560fb00240818efde6321c9095f6b4a6 ./internal/compiler/discover.go
|
||||
@@ -59,6 +41,8 @@ eefb05a35bd07660a293c8af97949cd6f69a22709728f3fe2cc9132b863b7d5a ./internal/com
|
||||
d7d8181455d5f37ef9bcc6bdbf86e0630f20e8a5b3b81688d12742687b434c99 ./internal/compiler/parse.go
|
||||
80cf170514a3b955d24440cb086d34e19f3a305510e3c5db95cb897be91f922a ./internal/compiler/replace_unix.go
|
||||
0fff1c67447bf5353ed1df6e7dfc4b14581b67adc1bf02f7a4a7c1f2680c392f ./internal/compiler/replace_windows.go
|
||||
f4ba01010ed5f5ba1e979702d82e95312bc0a4b13cc205c098926839be4ecb73 ./internal/compiler/testdata/golden/basic.sando
|
||||
b190a6a8aed288378ea13d12ec06bac68890c473c03c60016f7fa7534f142008 ./internal/compiler/testdata/golden/basic.sando.go
|
||||
eafbe9f7d8abb8fa792ec9e01f56655f9ec9d67279ffaac66d6035f9b2bfc404 ./internal/devserver/config.go
|
||||
99807040a870dd38ad1e04ae179243316778f94a41feb5d2c3076d463f52f9fe ./internal/devserver/config_test.go
|
||||
eddac51aecaac99bd11cfcf98f8a47cec5d51672efedad75d6f2a862c5d57fc1 ./internal/devserver/events.go
|
||||
@@ -85,14 +69,9 @@ ff76daee5b642ad84af31701833246d68b54d09580192312d750a7a2e893a692 ./sando/go.mod
|
||||
80ff53787919e809b8085d6ad9c3e183c9c7c1d74cfeda73369ac5c4607c236f ./sando/trust.go
|
||||
85621a44c730582f4410ac2c70418b739fb55e916f7e6b73a1a619982c459572 ./sando/write.go
|
||||
b188917e258890e6b6e4840a6fd946fc9a77cabc2068da3764f221e4a6a5df97 ./sando/write_test.go
|
||||
2d92e1578d8907ab434f1924f698b586f47d6c9bc6fab989639a5332fd235163 ./scripts/README.md
|
||||
032832440431a532771054bcbfb6c46944ead61bfdb38ca11a049aae14459bc3 ./scripts/check-licenses.sh
|
||||
e35895423494018d5e380c447a8fb9bd5410af82a4a12f9fbaae391c286dd32a ./scripts/check-site.sh
|
||||
d53d93df7e355ec45e6540703c6098c3fb49a610cb35600c5d525afa0f0d48cb ./scripts/release-check.sh
|
||||
be79b6bd1d18ce53a1b4cf301f7c4fa41342728d0a6333a766171cfc586a65d5 ./scripts/verify-public-install.sh
|
||||
b52ff6d1e0db9c4a72a587fc063d8022c51a439158679029c8df613b51905e51 ./scripts/verify.ps1
|
||||
5163a360b212de5abadeaa48db415e6f605e8b2a7f2fd2dc9ab7c1da19cec079 ./scripts/verify.sh
|
||||
7750a055be12b18c826d033f266a615bc1acdbddcfe5193f70521f7270a3e333 ./site/README.md
|
||||
1b28cf572543d61f89e7636f74b94fe96916c0dc80847831a9f42b57bebb83cb ./site/assets/site.css
|
||||
ecb00ac7fac0e161a3e4629c17209ddfaf0fbb8c78fd2615c2a7372808ae7cf7 ./site/index.html
|
||||
63cf8fc6f059c6a1164a70e9fd35564ff107282e01030d997f4e3e1e3dc0fdda ./site/sando/index.html
|
||||
2655a3f62772f7b3ef694aea2d03bd99e157d2449c232c7439c258ec36367fb1 ./scripts/README.md
|
||||
0bc796f71c863aa898674a26c56f055e3d81cf20629ca7b32fbae87d8841e0a8 ./scripts/check-licenses.sh
|
||||
502da1760bc87f260d474d81fe8f015a6e198f8d1ebf96db6b3f460ce4ef3b02 ./scripts/release-check.sh
|
||||
6be2fc6b8c3e0a7dd85437fa4089e75b772baa7556675081e6956a1b9f207fb4 ./scripts/verify-public-install.sh
|
||||
24ed3c9a1d37e46a856cbbd68e5c58ae04c6c9852902b99ed675e1f428339a9f ./scripts/verify.ps1
|
||||
f0cbd86759fa729064cb1c69991db2ac291792dadb6b1e1ba83794f2e390404d ./scripts/verify.sh
|
||||
|
||||
@@ -36,22 +36,18 @@ request object, or production server.
|
||||
|
||||
## Status
|
||||
|
||||
This repository is an unsupported public pre-1.0 source preview, not a supported v1 release. EQL Wiki remains the proof-of-production proving ground, and v1 is gated on security testing, cross-platform determinism, and a 14-day production soak with no renderer, security, or accessibility regression.
|
||||
|
||||
[sandwichhime.com](https://sandwichhime.com/) is the running self-hosted proof:
|
||||
its pages begin as `.sando`, compile into ordinary Go, and ship in an ordinary
|
||||
Go service whose production binary links only the `sando` runtime—not this
|
||||
compiler.
|
||||
This repository is an unsupported public pre-1.0 source preview, not a
|
||||
supported v1 release. V1 is gated only by repository-owned compiler, runtime,
|
||||
security, compatibility, and release checks. Application-specific deployments,
|
||||
examples, and case studies live in their own repositories and are not imported
|
||||
as release evidence here.
|
||||
|
||||
For repository development:
|
||||
|
||||
```sh
|
||||
go install ./cmd/himesan
|
||||
himesan generate ./examples/eql-shaped
|
||||
himesan check ./examples/eql-shaped
|
||||
go test ./...
|
||||
(cd sando && go test ./...)
|
||||
(cd examples/eql-shaped && himesan dev --config himesan.json)
|
||||
./scripts/verify.sh
|
||||
./scripts/check-licenses.sh
|
||||
```
|
||||
|
||||
The portable path is `generate`, `check`, and the project's normal Go tools.
|
||||
@@ -123,5 +119,7 @@ participation does not confer ownership of the identity or project.
|
||||
|
||||
The fuller origin, Japanese craft inspirations, family dedication, human-art
|
||||
commitment, and stewardship boundary live on the
|
||||
[project site](https://sandwichhime.com/docs/project/). Performance claims will
|
||||
follow published measurements, never precede them.
|
||||
[project site](https://sandwichhime.com/docs/project/). Tutorials and copyable
|
||||
applications are maintained separately from this compiler repository.
|
||||
Performance claims will follow repository-owned measurements, never precede
|
||||
them.
|
||||
|
||||
+19
-2
@@ -6,11 +6,23 @@ Sandwich Hime uses separate root and runtime version lines. Compiler tags are `v
|
||||
|
||||
The public pre-1.0 source snapshot is not a supported release and does not imply that the v1 gates below have passed.
|
||||
|
||||
No v1.0.0 release occurs until every gate in this repository is evidenced, including cross-platform deterministic generation, temporary-module compilation, fuzz/adversarial suites, race/vet/vulnerability/license checks on the latest two supported Go lines, development-supervisor failure tests, an EQL differential pilot, and a completed 14-day production soak without Hime render failures or security/accessibility regression.
|
||||
No v1.0.0 release occurs until every gate in this repository is evidenced,
|
||||
including cross-platform deterministic generation, temporary-module
|
||||
compilation, fuzz/adversarial suites, race/vet/vulnerability/license checks on
|
||||
the latest two supported Go lines, development-supervisor failure tests, and
|
||||
reproducible repository-owned benchmark and security results. A deployment,
|
||||
example, or case study in another repository is neither imported nor required
|
||||
as release evidence.
|
||||
|
||||
Release candidates require a clean canonical checkout, reviewed changelog, compatible vanity-import metadata, reproducible binaries, signed annotated tags, checksums, SBOMs, vulnerability results, and verification on Linux, macOS, and Windows. The runtime is tagged and published independently before the compiler that references its ABI.
|
||||
|
||||
Gitea is the only canonical public forge. Public source is exported into a separate, sanitized Gitea repository with fresh history; private development history and the private-to-public commit mapping are not published. Release binaries and provenance are built from the reviewed public commit. Publishing documentation, binaries, runtime tag, EQL mark, and case study is one coordinated v1 launch step.
|
||||
Gitea is the only canonical public forge. Public source is exported into a
|
||||
separate, sanitized Gitea repository with fresh history; private development
|
||||
history and the private-to-public commit mapping are not published. Release
|
||||
binaries and provenance are built from the reviewed public commit. Compiler
|
||||
documentation, binaries, checksums, SBOMs, and the independently versioned
|
||||
runtime tag form the coordinated v1 release. Example applications and product
|
||||
sites keep their own history, deployment, and evidence.
|
||||
|
||||
The hosting configuration must answer exact package discovery requests, not
|
||||
only module-root pages. In particular,
|
||||
@@ -22,3 +34,8 @@ fresh direct-fetch and public-proxy caches. This post-publication check is
|
||||
separate from the pre-tag, read-only `scripts/release-check.sh`.
|
||||
|
||||
Release notes report hardware, commit, datasets, commands, `ns/op`, allocations, response latency, and methodology for performance claims. “Fastest” or equivalent language is prohibited without durable, reproducible evidence.
|
||||
|
||||
Production applications compile and deploy their committed `.sando.go` files
|
||||
with the Apache-2.0 `sando` runtime. They do not need the AGPL compiler or the
|
||||
local development supervisor. Release checks verify that boundary without
|
||||
executing or inspecting an unrelated application repository.
|
||||
|
||||
+9
-10
@@ -6,7 +6,7 @@ Unchecked items are release blockers, not aspirational marketing.
|
||||
|
||||
## Compiler and runtime
|
||||
|
||||
- [ ] Deterministic golden output repeated across Linux, macOS, and Windows.
|
||||
- [ ] Compiler-owned deterministic golden output repeated across Linux, macOS, and Windows.
|
||||
- [ ] Temporary consumer modules compile using committed Go and only the Apache runtime.
|
||||
- [ ] Parser, delimiter, context, path, and source-map fuzz targets survive the release campaign.
|
||||
- [ ] Adversarial escaping and filesystem cases are evidenced.
|
||||
@@ -20,19 +20,18 @@ Unchecked items are release blockers, not aspirational marketing.
|
||||
- [ ] CSP hash injection, fragment/API/download exclusion, and cache disabling pass.
|
||||
- [ ] Replaced and interrupted child processes leave no descendants on supported systems.
|
||||
|
||||
## EQL Wiki proof
|
||||
## Repository-owned release evidence
|
||||
|
||||
- [ ] Separate `codex/himesan-pilot` worktree created after compiler gates.
|
||||
- [ ] Shared layout/home, browse fragment, and item page reach differential parity.
|
||||
- [ ] Accessibility, CSP, links/forms, malicious values, status, caching, and fragments pass.
|
||||
- [ ] Blue/green renderer flag and slot-switch rollback verified.
|
||||
- [ ] Fourteen continuous production days complete with zero Hime renderer failure or security/accessibility regression.
|
||||
- [ ] Honest before/after case study and reproducible benchmark report approved.
|
||||
- [ ] Contextual escaping is differentially tested against Go's documented `html/template` safety baseline.
|
||||
- [ ] Repository-owned synthetic benchmark cases and methodology are reproducible from a clean checkout.
|
||||
- [ ] Generated output is reviewed for stable provenance, source mappings, and absence of compiler-license headers.
|
||||
- [ ] Production application boundaries are documented: committed generated Go plus the Apache runtime, with no compiler or development supervisor in the deployed binary.
|
||||
- [ ] Unsupported or unmeasured performance and production claims are absent from release materials.
|
||||
|
||||
## Public launch
|
||||
|
||||
- [ ] Ownership notices, output permission, DCO contribution process, and pre-registration trademark terms receive final human review.
|
||||
- [ ] Name clearance, security mailbox, two-person credential recovery, and signing keys complete.
|
||||
- [ ] Gamertan vanity metadata and documentation verified from a clean machine.
|
||||
- [ ] `gamertan.com` vanity-import metadata and documented installs verified from a clean machine.
|
||||
- [ ] Sanitized fresh-history public Gitea snapshot contains no private paths, identifiers, history, or unsupported release claims.
|
||||
- [ ] Canonical public Gitea source preview and Gamertan documentation launch together with no secondary forge mirror.
|
||||
- [ ] Canonical public Gitea source and project documentation launch with no secondary forge mirror.
|
||||
|
||||
+8
-2
@@ -4,6 +4,12 @@
|
||||
|
||||
Benchmarks compare equivalent typed views and output against Go's `html/template` baseline. Reports include hardware, operating system, Go version, repository commit, dataset identity, exact commands, warmup/run counts, `ns/op`, bytes and allocations per operation, end-to-end response latency where relevant, output size, and statistical method.
|
||||
|
||||
The v1 gate is no material regression for the selected EQL pages under the published method. Only reproduced improvements become marketing claims. Microbenchmarks do not justify claims about request throughput, database-heavy pages, or whole-application latency.
|
||||
The v1 gate is no material regression against equivalent repository-owned
|
||||
synthetic cases under the published method. Only reproduced improvements become
|
||||
marketing claims. Microbenchmarks do not justify claims about request
|
||||
throughput, database-heavy pages, or whole-application latency.
|
||||
|
||||
Benchmark fixtures must contain synthetic or approved public data. The EQL production database is never copied into this repository or a public artifact.
|
||||
Benchmark fixtures must be self-contained, synthetic, reviewable, and committed
|
||||
to this repository. Application-specific datasets and deployment measurements
|
||||
belong with their applications and are neither copied here nor treated as core
|
||||
release gates.
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
Zero-Clause BSD
|
||||
|
||||
Copyright (c) 2025-2026 Cole Speelman
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
@@ -1,15 +0,0 @@
|
||||
<!-- SPDX-FileCopyrightText: 2025-2026 Cole Speelman -->
|
||||
<!-- SPDX-License-Identifier: 0BSD -->
|
||||
|
||||
# Example license map
|
||||
|
||||
The original files in this synthetic, copyable example are licensed under the
|
||||
0BSD terms in `LICENSE`, including `.sando` templates, handwritten Go,
|
||||
`himesan.json`, and committed `.sando.go` output.
|
||||
|
||||
The generated files intentionally have no inline SPDX header because Hime-san
|
||||
replaces the whole file. This module-level license map covers them. Generator
|
||||
scaffolding copied into those files also has the additional permission in the
|
||||
root repository's `OUTPUT_EXCEPTION.md`; that permission is not required to
|
||||
keep this example under 0BSD when the example author already owns the relevant
|
||||
input code.
|
||||
@@ -1,15 +0,0 @@
|
||||
<!-- SPDX-License-Identifier: 0BSD -->
|
||||
|
||||
# Synthetic EQL-shaped fixture
|
||||
|
||||
This copyable 0BSD example exercises a shared document layout, typed page data,
|
||||
component composition, loops, text escaping, attribute escaping, and URL
|
||||
policy without containing EQL Wiki code, data, routes, or its database.
|
||||
|
||||
From the repository root:
|
||||
|
||||
```sh
|
||||
go run ./cmd/himesan generate ./examples/eql-shaped
|
||||
go run ./cmd/himesan check ./examples/eql-shaped
|
||||
(cd examples/eql-shaped && go test ./... && go run ./cmd/example)
|
||||
```
|
||||
@@ -1,83 +0,0 @@
|
||||
// SPDX-License-Identifier: 0BSD
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"example.com/eql-shaped/views"
|
||||
"gamertan.com/sandwich-hime/sando"
|
||||
)
|
||||
|
||||
func main() {
|
||||
address := os.Getenv("HIMESAN_LISTEN_ADDR")
|
||||
if address != "" {
|
||||
serve(address)
|
||||
return
|
||||
}
|
||||
|
||||
output, err := renderPage(context.Background())
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
_, _ = output.WriteTo(os.Stdout)
|
||||
}
|
||||
|
||||
func serve(address string) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("GET /healthz", func(response http.ResponseWriter, _ *http.Request) {
|
||||
response.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
response.WriteHeader(http.StatusOK)
|
||||
_, _ = response.Write([]byte("ok\n"))
|
||||
})
|
||||
mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
|
||||
output, err := renderPage(request.Context())
|
||||
if err != nil {
|
||||
http.Error(response, "render failed", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
response.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
response.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'self'")
|
||||
response.WriteHeader(http.StatusOK)
|
||||
_, _ = output.WriteTo(response)
|
||||
})
|
||||
|
||||
server := &http.Server{Addr: address, Handler: mux, ReadHeaderTimeout: 5 * time.Second}
|
||||
if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func renderPage(ctx context.Context) (*bytes.Buffer, error) {
|
||||
body := views.Home(views.HomeView{
|
||||
Heading: "EQL-shaped records",
|
||||
Intro: "Typed markup without making the template compiler your web framework.",
|
||||
Browse: views.BrowseView{
|
||||
Query: "pioneer & archivist",
|
||||
Records: []views.RecordView{
|
||||
{URL: "/items/1?from=home&kind=book", Title: "A <field> guide", Kind: "book", Featured: true},
|
||||
{URL: "/items/2", Title: "Community memory", Kind: "archive"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
page := views.Layout(views.LayoutView{
|
||||
SiteName: "EQL Wiki Fixture",
|
||||
Title: "Home",
|
||||
Body: body,
|
||||
})
|
||||
|
||||
var output bytes.Buffer
|
||||
if err := sando.Render(ctx, &output, page); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &output, nil
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// SPDX-License-Identifier: 0BSD
|
||||
|
||||
module example.com/eql-shaped
|
||||
|
||||
go 1.25
|
||||
|
||||
require gamertan.com/sandwich-hime/sando v0.0.0
|
||||
|
||||
replace gamertan.com/sandwich-hime/sando => ../../sando
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"sourceRoots": ["views"],
|
||||
"goPackage": "./cmd/example",
|
||||
"listenAddressEnv": "HIMESAN_LISTEN_ADDR",
|
||||
"healthPath": "/healthz",
|
||||
"proxyAddress": "127.0.0.1:7331",
|
||||
"additionalWatchRoots": []
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?sando go
|
||||
package views
|
||||
|
||||
func Badge(label string)
|
||||
?>
|
||||
<?# SPDX-License-Identifier: 0BSD ?>
|
||||
<strong class="badge"><?= label ?></strong>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?sando go
|
||||
package views
|
||||
|
||||
func BrowseResults(view BrowseView)
|
||||
?>
|
||||
<?# SPDX-License-Identifier: 0BSD ?>
|
||||
<section aria-label="Browse results" data-query="<?= view.Query ?>">
|
||||
<ul>
|
||||
<? for _, record := range view.Records { ?>
|
||||
<li>
|
||||
<a href="<?= record.URL ?>"><?= record.Title ?></a>
|
||||
<span class="kind"><?= record.Kind ?></span>
|
||||
<? if record.Featured { ?><?~ Badge("featured") ?><? } ?>
|
||||
</li>
|
||||
<? } ?>
|
||||
</ul>
|
||||
</section>
|
||||
@@ -1,85 +0,0 @@
|
||||
// Code generated by himesan; DO NOT EDIT.
|
||||
// himesan:compiler 0.1.0-dev
|
||||
// himesan:runtime-abi sando.v1
|
||||
// himesan:source-sha256 c5b402971618747c906c84508d5af9fd00aa33391b9b59f093b001a3cb0b2164
|
||||
|
||||
package views
|
||||
|
||||
import (
|
||||
__himesan_context "context"
|
||||
__himesan_sando "gamertan.com/sandwich-hime/sando"
|
||||
__himesan_io "io"
|
||||
)
|
||||
|
||||
var _ = __himesan_sando.ABI
|
||||
|
||||
func BrowseResults(view BrowseView) __himesan_sando.Component {
|
||||
return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error {
|
||||
_ = __himesan_render_context
|
||||
//line views/browse_results.sando:5:3
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:6:37
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n<section aria-label=\"Browse results\" data-query=\""); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:7:54
|
||||
if __himesan_error := __himesan_sando.WriteAttr(__himesan_writer, (view.Query)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:7:67
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\">\n <ul>\n "); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:9:6
|
||||
for _, record := range view.Records {
|
||||
//line views/browse_results.sando:9:46
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n <li>\n <a href=\""); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:11:20
|
||||
if __himesan_error := __himesan_sando.WriteURL(__himesan_writer, (record.URL)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:11:33
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\">"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:11:39
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (record.Title)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:11:54
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</a>\n <span class=\"kind\">"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:12:30
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (record.Kind)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:12:44
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</span>\n "); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:13:10
|
||||
if record.Featured {
|
||||
//line views/browse_results.sando:13:37
|
||||
if __himesan_error := __himesan_sando.Render(__himesan_render_context, __himesan_writer, (Badge("featured"))); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:13:60
|
||||
}
|
||||
//line views/browse_results.sando:13:64
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n </li>\n "); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:15:6
|
||||
}
|
||||
//line views/browse_results.sando:15:10
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n </ul>\n</section>\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<?sando go
|
||||
package views
|
||||
|
||||
func Home(view HomeView)
|
||||
?>
|
||||
<?# SPDX-License-Identifier: 0BSD ?>
|
||||
<section aria-labelledby="home-heading">
|
||||
<h1 id="home-heading"><?= view.Heading ?></h1>
|
||||
<p><?= view.Intro ?></p>
|
||||
<?~ BrowseResults(view.Browse) ?>
|
||||
</section>
|
||||
@@ -1,53 +0,0 @@
|
||||
// Code generated by himesan; DO NOT EDIT.
|
||||
// himesan:compiler 0.1.0-dev
|
||||
// himesan:runtime-abi sando.v1
|
||||
// himesan:source-sha256 696c391538cdc50c5ab1abea23b561fa354e56eaf73b7ad6aabe130389715a8c
|
||||
|
||||
package views
|
||||
|
||||
import (
|
||||
__himesan_context "context"
|
||||
__himesan_sando "gamertan.com/sandwich-hime/sando"
|
||||
__himesan_io "io"
|
||||
)
|
||||
|
||||
var _ = __himesan_sando.ABI
|
||||
|
||||
func Home(view HomeView) __himesan_sando.Component {
|
||||
return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error {
|
||||
_ = __himesan_render_context
|
||||
//line views/home.sando:5:3
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/home.sando:6:37
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n<section aria-labelledby=\"home-heading\">\n <h1 id=\"home-heading\">"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/home.sando:8:29
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (view.Heading)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/home.sando:8:44
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</h1>\n <p>"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/home.sando:9:10
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (view.Intro)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/home.sando:9:23
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</p>\n "); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/home.sando:10:7
|
||||
if __himesan_error := __himesan_sando.Render(__himesan_render_context, __himesan_writer, (BrowseResults(view.Browse))); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/home.sando:10:36
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n</section>\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?sando go
|
||||
package views
|
||||
|
||||
func Layout(view LayoutView)
|
||||
?>
|
||||
<?# SPDX-License-Identifier: 0BSD ?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title><?= view.Title ?> · <?= view.SiteName ?></title>
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main">Skip to content</a>
|
||||
<header><a href="/"><?= view.SiteName ?></a></header>
|
||||
<main id="main"><?~ view.Body ?></main>
|
||||
<footer>Built with Sandwich Hime.</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,61 +0,0 @@
|
||||
// Code generated by himesan; DO NOT EDIT.
|
||||
// himesan:compiler 0.1.0-dev
|
||||
// himesan:runtime-abi sando.v1
|
||||
// himesan:source-sha256 c9a8102588cb94f946a36414291d12cb681e605acc93c2b5dc8d74fd33ae5603
|
||||
|
||||
package views
|
||||
|
||||
import (
|
||||
__himesan_context "context"
|
||||
__himesan_sando "gamertan.com/sandwich-hime/sando"
|
||||
__himesan_io "io"
|
||||
)
|
||||
|
||||
var _ = __himesan_sando.ABI
|
||||
|
||||
func Layout(view LayoutView) __himesan_sando.Component {
|
||||
return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error {
|
||||
_ = __himesan_render_context
|
||||
//line views/layout.sando:5:3
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:6:37
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:12:14
|
||||
if __himesan_error := __himesan_sando.WriteRCDATA(__himesan_writer, (view.Title)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:12:27
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, " · "); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:12:35
|
||||
if __himesan_error := __himesan_sando.WriteRCDATA(__himesan_writer, (view.SiteName)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:12:51
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</title>\n</head>\n<body>\n <a href=\"#main\">Skip to content</a>\n <header><a href=\"/\">"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:16:27
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (view.SiteName)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:16:43
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</a></header>\n <main id=\"main\">"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:17:23
|
||||
if __himesan_error := __himesan_sando.Render(__himesan_render_context, __himesan_writer, (view.Body)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:17:35
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</main>\n <footer>Built with Sandwich Hime.</footer>\n</body>\n</html>\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// SPDX-License-Identifier: 0BSD
|
||||
|
||||
package views
|
||||
|
||||
import "gamertan.com/sandwich-hime/sando"
|
||||
|
||||
type LayoutView struct {
|
||||
SiteName string
|
||||
Title string
|
||||
Body sando.Component
|
||||
}
|
||||
|
||||
type HomeView struct {
|
||||
Heading string
|
||||
Intro string
|
||||
Browse BrowseView
|
||||
}
|
||||
|
||||
type BrowseView struct {
|
||||
Query string
|
||||
Records []RecordView
|
||||
}
|
||||
|
||||
type RecordView struct {
|
||||
URL string
|
||||
Title string
|
||||
Kind string
|
||||
Featured bool
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
// SPDX-License-Identifier: 0BSD
|
||||
|
||||
package views
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"html/template"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gamertan.com/sandwich-hime/sando"
|
||||
)
|
||||
|
||||
func TestBrowseResultsEscapesUntrustedValues(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
component := BrowseResults(BrowseView{
|
||||
Query: `"><script>alert(1)</script>`,
|
||||
Records: []RecordView{{
|
||||
URL: `/item?q=" onclick="alert(1)`,
|
||||
Title: `<img src=x onerror=alert(1)>`,
|
||||
Kind: `" aria-label="injected`,
|
||||
}},
|
||||
})
|
||||
|
||||
var output bytes.Buffer
|
||||
if err := sando.Render(context.Background(), &output, component); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := output.String()
|
||||
if strings.Contains(got, "<script>") || strings.Contains(got, "<img") || strings.Contains(got, `" onclick=`) {
|
||||
t.Fatalf("untrusted markup survived escaping: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBrowseResultsRejectsDangerousURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
component := BrowseResults(BrowseView{Records: []RecordView{{
|
||||
URL: " JaVaScRiPt:\talert(1)",
|
||||
Title: "unsafe",
|
||||
}}})
|
||||
var output bytes.Buffer
|
||||
err := sando.Render(context.Background(), &output, component)
|
||||
if !errors.Is(err, sando.ErrUnsafeURL) {
|
||||
t.Fatalf("expected unsafe URL error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeneratedComponentPropagatesWriterFailures(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
component := Badge("proof")
|
||||
if err := sando.Render(context.Background(), shortWriter{}, component); !errors.Is(err, io.ErrShortWrite) {
|
||||
t.Fatalf("short writer error = %v, want io.ErrShortWrite", err)
|
||||
}
|
||||
|
||||
want := errors.New("connection closed")
|
||||
if err := sando.Render(context.Background(), errorWriter{err: want}, component); !errors.Is(err, want) {
|
||||
t.Fatalf("writer error = %v, want %v", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextEscapingAgainstHTMLTemplateBaseline(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
baseline := template.Must(template.New("probe").Parse(`<a href="{{ .URL }}" title="{{ .Title }}">{{ .Text }}</a>`))
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
title string
|
||||
text string
|
||||
}{
|
||||
{name: "entities", url: "/items?q=one&kind=book", title: `say "hello" & goodbye`, text: `<strong>not markup</strong>`},
|
||||
{name: "unicode", url: "/items/cole", title: "Café & 雪", text: "Cole's archive ♥"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
data := struct{ URL, Title, Text string }{test.url, test.title, test.text}
|
||||
var legacy bytes.Buffer
|
||||
if err := baseline.Execute(&legacy, data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var generated bytes.Buffer
|
||||
if err := sando.Render(context.Background(), &generated, SecurityProbe(test.url, test.title, test.text)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := strings.TrimSpace(generated.String()), legacy.String(); got != want {
|
||||
t.Fatalf("generated escaping differs from html/template baseline\n got: %s\nwant: %s", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDangerousURLIsBlockedAtLeastAsStrictlyAsHTMLTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const dangerous = " JaVaScRiPt:\talert(1)"
|
||||
baseline := template.Must(template.New("probe").Parse(`<a href="{{ . }}">link</a>`))
|
||||
var legacy bytes.Buffer
|
||||
if err := baseline.Execute(&legacy, dangerous); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(legacy.String(), "#ZgotmplZ") {
|
||||
t.Fatalf("html/template baseline unexpectedly accepted dangerous URL: %s", legacy.String())
|
||||
}
|
||||
|
||||
var generated bytes.Buffer
|
||||
err := sando.Render(context.Background(), &generated, SecurityProbe(dangerous, "title", "link"))
|
||||
if !errors.Is(err, sando.ErrUnsafeURL) {
|
||||
t.Fatalf("generated renderer error = %v, want ErrUnsafeURL", err)
|
||||
}
|
||||
}
|
||||
|
||||
type shortWriter struct{}
|
||||
|
||||
func (shortWriter) Write(value []byte) (int, error) {
|
||||
if len(value) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return len(value) - 1, nil
|
||||
}
|
||||
|
||||
type errorWriter struct{ err error }
|
||||
|
||||
func (writer errorWriter) Write([]byte) (int, error) { return 0, writer.err }
|
||||
@@ -1,7 +0,0 @@
|
||||
<?sando go
|
||||
package views
|
||||
|
||||
func SecurityProbe(url string, title string, text string)
|
||||
?>
|
||||
<?# SPDX-License-Identifier: 0BSD ?>
|
||||
<a href="<?= url ?>" title="<?= title ?>"><?= text ?></a>
|
||||
@@ -1,53 +0,0 @@
|
||||
// Code generated by himesan; DO NOT EDIT.
|
||||
// himesan:compiler 0.1.0-dev
|
||||
// himesan:runtime-abi sando.v1
|
||||
// himesan:source-sha256 8d59b45799d0ed2e9e6997f286315f2cb794ea660940978ec39efa3b9a3b94ab
|
||||
|
||||
package views
|
||||
|
||||
import (
|
||||
__himesan_context "context"
|
||||
__himesan_sando "gamertan.com/sandwich-hime/sando"
|
||||
__himesan_io "io"
|
||||
)
|
||||
|
||||
var _ = __himesan_sando.ABI
|
||||
|
||||
func SecurityProbe(url string, title string, text string) __himesan_sando.Component {
|
||||
return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error {
|
||||
_ = __himesan_render_context
|
||||
//line views/security_probe.sando:5:3
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/security_probe.sando:6:37
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n<a href=\""); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/security_probe.sando:7:14
|
||||
if __himesan_error := __himesan_sando.WriteURL(__himesan_writer, (url)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/security_probe.sando:7:20
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\" title=\""); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/security_probe.sando:7:33
|
||||
if __himesan_error := __himesan_sando.WriteAttr(__himesan_writer, (title)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/security_probe.sando:7:41
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\">"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/security_probe.sando:7:47
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (text)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/security_probe.sando:7:54
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</a>\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -63,10 +63,9 @@ func TestCommittedGoldenOutput(t *testing.T) {
|
||||
if !ok {
|
||||
t.Fatal("runtime.Caller failed")
|
||||
}
|
||||
root := filepath.Clean(filepath.Join(filepath.Dir(thisFile), "..", ".."))
|
||||
sourcePath := filepath.Join(root, "examples", "eql-shaped", "views", "badge.sando")
|
||||
sourcePath := filepath.Join(filepath.Dir(thisFile), "testdata", "golden", "basic.sando")
|
||||
wantPath := sourcePath + ".go"
|
||||
compiled, diagnostics := compileWithMapping(sourcePath, mustRead(t, sourcePath), "views/badge.sando")
|
||||
compiled, diagnostics := compileWithMapping(sourcePath, mustRead(t, sourcePath), "internal/compiler/testdata/golden/basic.sando")
|
||||
assertNoErrorDiagnostics(t, diagnostics)
|
||||
if want := mustRead(t, wantPath); !bytes.Equal(compiled.Code, want) {
|
||||
t.Fatalf("committed golden output is stale; run himesan generate\n--- got ---\n%s\n--- want ---\n%s", compiled.Code, want)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<?sando go
|
||||
package golden
|
||||
|
||||
func Greeting(name string)
|
||||
?>
|
||||
<?# SPDX-License-Identifier: AGPL-3.0-only ?>
|
||||
<p class="greeting">Hello, <?= name ?>.</p>
|
||||
Vendored
+10
-10
@@ -1,9 +1,9 @@
|
||||
// Code generated by himesan; DO NOT EDIT.
|
||||
// himesan:compiler 0.1.0-dev
|
||||
// himesan:runtime-abi sando.v1
|
||||
// himesan:source-sha256 2c0063ed8724520f146d6717bb7d828ce69be17b41cbfb8f4866664ba09a58a8
|
||||
// himesan:source-sha256 f4ba01010ed5f5ba1e979702d82e95312bc0a4b13cc205c098926839be4ecb73
|
||||
|
||||
package views
|
||||
package golden
|
||||
|
||||
import (
|
||||
__himesan_context "context"
|
||||
@@ -13,23 +13,23 @@ import (
|
||||
|
||||
var _ = __himesan_sando.ABI
|
||||
|
||||
func Badge(label string) __himesan_sando.Component {
|
||||
func Greeting(name string) __himesan_sando.Component {
|
||||
return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error {
|
||||
_ = __himesan_render_context
|
||||
//line views/badge.sando:5:3
|
||||
//line internal/compiler/testdata/golden/basic.sando:5:3
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/badge.sando:6:37
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n<strong class=\"badge\">"); __himesan_error != nil {
|
||||
//line internal/compiler/testdata/golden/basic.sando:6:46
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n<p class=\"greeting\">Hello, "); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/badge.sando:7:27
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (label)); __himesan_error != nil {
|
||||
//line internal/compiler/testdata/golden/basic.sando:7:32
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (name)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/badge.sando:7:35
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</strong>\n"); __himesan_error != nil {
|
||||
//line internal/compiler/testdata/golden/basic.sando:7:39
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, ".</p>\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
return nil
|
||||
+2
-4
@@ -4,11 +4,9 @@
|
||||
|
||||
These scripts are intentionally understandable shell and PowerShell rather than a release framework with hidden defaults.
|
||||
|
||||
- `verify.sh` runs root and nested-module tests and vet, builds `himesan`, tests each example module, checks committed generated output, and proves two generation passes leave the same bytes and unchanged modification times. Set `HIMESAN_RACE=1` for race tests.
|
||||
- `verify.sh` runs root and nested-module tests and vet, builds `himesan`, checks the compiler-owned golden output, and proves two generation passes leave the same bytes and unchanged modification times. Set `HIMESAN_RACE=1` for race tests.
|
||||
- `verify.ps1` provides the equivalent native Windows lane; pass `-Race` to include the race detector.
|
||||
- `check-licenses.sh` enforces the AGPL compiler / Apache runtime / 0BSD example boundary and prevents generated application Go from inheriting an AGPL identifier.
|
||||
- `check-site.sh` verifies vanity metadata, no-script/local-asset policy, pre-release honesty, and baseline accessibility scaffolding for the static site.
|
||||
- `eql-integration.sh` is opt-in. Until a repository-owned `internal/integration/eql` test exists it exits successfully without inspecting EQL. Later it requires external `HIMESAN_EQL_ROOT` and `HIMESAN_EQL_DB` paths and passes a read-only-mode contract to that package. It never copies a database.
|
||||
- `check-licenses.sh` enforces the AGPL compiler / Apache runtime boundary and prevents generated application Go from inheriting an AGPL identifier.
|
||||
- `release-check.sh --version vX.Y.Z` is a clean-checkout technical preflight. Add `--public` and point `HIMESAN_RELEASE_EVIDENCE_DIR` at a human-reviewed evidence bundle for the public-launch gate. It never tags, pushes, publishes, or deploys.
|
||||
- `verify-public-install.sh --version vX.Y.Z` is a post-tag/publication check. It verifies exact `go-get=1` package routes and runs the documented compiler install and runtime get from fresh direct-fetch and public-proxy caches without interactive Git credentials.
|
||||
|
||||
|
||||
@@ -15,9 +15,6 @@ fail() {
|
||||
|
||||
expected_spdx() {
|
||||
case "$1" in
|
||||
examples/*)
|
||||
printf '0BSD\n'
|
||||
;;
|
||||
sando/*)
|
||||
printf 'Apache-2.0\n'
|
||||
;;
|
||||
@@ -77,7 +74,6 @@ list_project_files() {
|
||||
|
||||
[[ -f LICENSE ]] || fail 'root LICENSE is missing'
|
||||
[[ -f sando/LICENSE ]] || fail 'sando/LICENSE is missing'
|
||||
[[ -f examples/eql-shaped/LICENSE ]] || fail 'example LICENSE is missing'
|
||||
[[ -f DCO.txt ]] || fail 'DCO.txt is missing'
|
||||
|
||||
if [[ -f LICENSE ]]; then
|
||||
@@ -86,16 +82,11 @@ fi
|
||||
if [[ -f sando/LICENSE ]]; then
|
||||
check_sha256 sando/LICENSE c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4
|
||||
fi
|
||||
if [[ -f examples/eql-shaped/LICENSE ]]; then
|
||||
check_sha256 examples/eql-shaped/LICENSE 7e2406acc98391ec126b13d512c00b930bbc2c19f4d5b0fe52286ae41bfbc92d
|
||||
grep -Fq 'Copyright (c) 2025-2026 Cole Speelman' examples/eql-shaped/LICENSE || \
|
||||
fail 'example LICENSE must identify its original author'
|
||||
fi
|
||||
if [[ -f DCO.txt ]]; then
|
||||
check_sha256 DCO.txt f7ac75b443f4ca16b503241344b41aeff9503b0c30bedc2b119551d83cb0fa90
|
||||
fi
|
||||
|
||||
for required in COPYRIGHT OUTPUT_EXCEPTION.md sando/COPYRIGHT examples/eql-shaped/LICENSES.md; do
|
||||
for required in COPYRIGHT OUTPUT_EXCEPTION.md sando/COPYRIGHT; do
|
||||
[[ -f $required ]] || fail "$required is required for ownership/output licensing"
|
||||
done
|
||||
|
||||
@@ -126,7 +117,7 @@ while IFS= read -r -d '' path; do
|
||||
[[ -f $path ]] || continue
|
||||
|
||||
case "$path" in
|
||||
LICENSE | sando/LICENSE | examples/*/LICENSE | DCO.txt)
|
||||
LICENSE | sando/LICENSE | DCO.txt)
|
||||
# These are reviewed legal texts with their own notices.
|
||||
continue
|
||||
;;
|
||||
@@ -161,9 +152,7 @@ while IFS= read -r -d '' path; do
|
||||
continue
|
||||
;;
|
||||
*.json)
|
||||
if [[ $path != examples/eql-shaped/himesan.json ]]; then
|
||||
fail "$path cannot carry a comment and needs an explicit license-map entry"
|
||||
fi
|
||||
fail "$path cannot carry a comment and needs an explicit license-map entry"
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
@@ -178,23 +167,6 @@ while IFS= read -r -d '' path; do
|
||||
fail "$path must carry exactly one SPDX identifier: $expected"
|
||||
done < <(list_project_files)
|
||||
|
||||
if [[ -f examples/eql-shaped/LICENSES.md ]]; then
|
||||
grep -Fq '`himesan.json`' examples/eql-shaped/LICENSES.md || \
|
||||
fail 'example license map must cover himesan.json'
|
||||
grep -Fq '`.sando.go`' examples/eql-shaped/LICENSES.md || \
|
||||
fail 'example license map must cover generated output'
|
||||
fi
|
||||
|
||||
if [[ -d examples ]] && find examples -type f -name go.mod -print -quit | grep -q .; then
|
||||
while IFS= read -r -d '' module_file; do
|
||||
module_dir=$(dirname -- "$module_file")
|
||||
license_file=$module_dir/LICENSE
|
||||
map_file=$module_dir/LICENSES.md
|
||||
[[ -f $license_file ]] || fail "example module $module_dir needs a local 0BSD LICENSE"
|
||||
[[ -f $map_file ]] || fail "example module $module_dir needs a local license map"
|
||||
done < <(find examples -type d \( -name .git -o -name vendor \) -prune -o -type f -name go.mod -print0)
|
||||
fi
|
||||
|
||||
if find sando -type f -name '*.go' -exec grep -En \
|
||||
'"gamertan\.com/sandwich-hime/(cmd|internal)(/|"|$)' {} + | grep -q .; then
|
||||
fail 'the Apache runtime imports AGPL compiler or CLI code'
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
repo_root=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
cd "$repo_root"
|
||||
|
||||
public_version=''
|
||||
if (( $# > 0 )); then
|
||||
if [[ "$1" != --public || $# -ne 2 ]]; then
|
||||
printf 'usage: scripts/check-site.sh [--public vX.Y.Z]\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
public_version=$2
|
||||
fi
|
||||
|
||||
fail() {
|
||||
printf 'site error: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
for page in site/index.html site/sando/index.html; do
|
||||
[[ -f "$page" ]] || fail "missing $page"
|
||||
grep -Fq '<html lang="en">' "$page" || fail "$page needs a document language"
|
||||
grep -Fq '<meta name="viewport"' "$page" || fail "$page needs responsive viewport metadata"
|
||||
grep -Fq 'class="skip-link"' "$page" || fail "$page needs a keyboard skip link"
|
||||
grep -Fq '<main id="main">' "$page" || fail "$page needs the skip-link target"
|
||||
grep -Fq '<h1>' "$page" || grep -Fq '<h1 ' "$page" || fail "$page needs an h1"
|
||||
grep -Fq 'Content-Security-Policy' "$page" || fail "$page needs a preview CSP"
|
||||
grep -Fq '<meta name="himesan-release-status" content="' "$page" || \
|
||||
fail "$page needs machine-readable release status"
|
||||
done
|
||||
|
||||
grep -Fq '<meta name="go-import" content="gamertan.com/sandwich-hime git https://gitea.speelman.ca/gamertan/sandwich-hime.git">' \
|
||||
site/index.html || fail 'compiler vanity-import metadata is missing or changed'
|
||||
grep -Fq '<meta name="go-import" content="gamertan.com/sandwich-hime/sando git https://gitea.speelman.ca/gamertan/sandwich-hime.git sando">' \
|
||||
site/sando/index.html || fail 'nested runtime vanity-import metadata is missing or changed'
|
||||
|
||||
if find site -type f -name '*.html' -exec grep -Ein '<script([[:space:]>])' {} + | grep -q .; then
|
||||
fail 'the static project site must not contain JavaScript'
|
||||
fi
|
||||
if find site -type f -name '*.html' -exec grep -Ein \
|
||||
'(src|href)="https?://[^" ]+\.(js|css)([?"#])' {} + | grep -q .; then
|
||||
fail 'the static project site must not load remote JavaScript or CSS'
|
||||
fi
|
||||
if find site -type f -name '*.css' -exec grep -Ein \
|
||||
"(@import|url\\()[[:space:]\"']*https?://" {} + | grep -q .; then
|
||||
fail 'the static project site must not load remote CSS assets'
|
||||
fi
|
||||
|
||||
grep -Fq 'prefers-reduced-motion' site/assets/site.css || fail 'site CSS needs a reduced-motion preference'
|
||||
grep -Fq 'forced-colors' site/assets/site.css || fail 'site CSS needs a forced-colors fallback'
|
||||
grep -Fq 'class="wordmark" role="img"' site/index.html || \
|
||||
fail 'the ASCII wordmark needs an accessible semantic role'
|
||||
grep -Fq 'class="code" tabindex="0" role="region"' site/index.html || \
|
||||
fail 'the scrollable code example needs keyboard access and a region role'
|
||||
|
||||
if [[ -n "$public_version" ]]; then
|
||||
for page in site/index.html site/sando/index.html; do
|
||||
grep -Fq "<meta name=\"himesan-release-status\" content=\"$public_version\">" "$page" || \
|
||||
fail "$page release status does not match $public_version"
|
||||
done
|
||||
if grep -Eiq 'not a public release|not released yet|private pre-release|public pre-1\.0|unsupported pre-1\.0|no (supported )?public .*tag' \
|
||||
site/index.html site/sando/index.html; then
|
||||
fail 'public-release site still contains a pre-release warning'
|
||||
fi
|
||||
else
|
||||
if grep -Fq '<meta name="himesan-release-status" content="pre-release">' site/index.html; then
|
||||
grep -Eiq 'public pre-1\.0|unsupported pre-1\.0|not released yet' site/index.html || \
|
||||
fail 'the pre-release landing page must state its status in human-readable text'
|
||||
fi
|
||||
fi
|
||||
|
||||
printf 'Static site metadata, local-asset policy, and accessibility scaffolding are present.\n'
|
||||
@@ -64,7 +64,7 @@ case "$origin_url" in
|
||||
https://gitea.speelman.ca/gamertan/sandwich-hime.git)
|
||||
;;
|
||||
*)
|
||||
printf 'error: origin is not the canonical Gamertan Gitea repository: %s\n' "$origin_url" >&2
|
||||
printf 'error: origin is not the canonical Gitea repository: %s\n' "$origin_url" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -131,9 +131,6 @@ for target in \
|
||||
done
|
||||
|
||||
for required in \
|
||||
site/index.html \
|
||||
site/sando/index.html \
|
||||
site/README.md \
|
||||
scripts/verify-public-install.sh \
|
||||
RELEASE.md \
|
||||
SECURITY.md \
|
||||
@@ -142,11 +139,6 @@ for required in \
|
||||
[[ -f "$required" ]] || { printf 'error: required release file is missing: %s\n' "$required" >&2; exit 1; }
|
||||
done
|
||||
|
||||
if ! grep -Fq 'gamertan.com/sandwich-hime/sando git' site/sando/index.html; then
|
||||
printf 'error: nested runtime vanity-import metadata is missing\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( public_release == 1 )); then
|
||||
evidence_dir=${HIMESAN_RELEASE_EVIDENCE_DIR:-}
|
||||
if [[ -z "$evidence_dir" || ! -d "$evidence_dir" ]]; then
|
||||
@@ -154,18 +146,18 @@ if (( public_release == 1 )); then
|
||||
exit 1
|
||||
fi
|
||||
for evidence in \
|
||||
counsel-review.md \
|
||||
legal-review.md \
|
||||
cross-platform.md \
|
||||
eql-production-soak.md \
|
||||
security-and-accessibility.md \
|
||||
security.md \
|
||||
development-supervisor.md \
|
||||
benchmark-methodology.md \
|
||||
vanity-and-mirror.md; do
|
||||
vanity-imports.md \
|
||||
signing-and-recovery.md; do
|
||||
if [[ ! -s "$evidence_dir/$evidence" ]]; then
|
||||
printf 'error: public release evidence is missing or empty: %s\n' "$evidence_dir/$evidence" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
./scripts/check-site.sh --public "$version"
|
||||
printf '\nHuman review is still required; evidence presence is not automatic approval.\n'
|
||||
else
|
||||
printf '\nTechnical preflight passed. Public launch remains blocked until --public evidence review passes.\n'
|
||||
|
||||
@@ -7,7 +7,7 @@ usage() {
|
||||
cat <<'EOF'
|
||||
Usage: scripts/verify-public-install.sh --version vX.Y.Z
|
||||
|
||||
Post-publication verification for Gamertan vanity metadata and the exact
|
||||
Post-publication verification for gamertan.com vanity metadata and the exact
|
||||
documented install commands. It uses fresh temporary Go caches and never writes
|
||||
to the repository. Signed compiler and sando tags must already be public.
|
||||
EOF
|
||||
|
||||
+7
-28
@@ -43,12 +43,11 @@ function Invoke-ModuleChecks {
|
||||
}
|
||||
|
||||
function Get-SandoSources {
|
||||
if (-not (Test-Path "examples" -PathType Container)) {
|
||||
if (-not (Test-Path "internal/compiler/testdata/golden" -PathType Container)) {
|
||||
return @()
|
||||
}
|
||||
|
||||
return @(Get-ChildItem "examples" -Recurse -File -Filter "*.sando" |
|
||||
Where-Object { $_.FullName -notmatch '[\\/](vendor|\.git)[\\/]' } |
|
||||
return @(Get-ChildItem "internal/compiler/testdata/golden" -File -Filter "*.sando" |
|
||||
Sort-Object FullName)
|
||||
}
|
||||
|
||||
@@ -80,29 +79,20 @@ try {
|
||||
}
|
||||
Invoke-ModuleChecks "sando" "sando runtime module"
|
||||
|
||||
if (Test-Path "examples" -PathType Container) {
|
||||
$ExampleModules = @(Get-ChildItem "examples" -Recurse -File -Filter "go.mod" |
|
||||
Where-Object { $_.FullName -notmatch '[\\/](vendor|\.git)[\\/]' } |
|
||||
Sort-Object FullName)
|
||||
}
|
||||
else {
|
||||
$ExampleModules = @()
|
||||
}
|
||||
|
||||
$Sources = @(Get-SandoSources)
|
||||
if ($Sources.Count -eq 0) {
|
||||
Write-Host "`n==> generation: no .sando examples exist yet; skipping deterministic-generation check"
|
||||
throw "compiler-owned golden .sando fixture is missing"
|
||||
}
|
||||
else {
|
||||
$SourcePaths = @($Sources | ForEach-Object { $_.FullName })
|
||||
$CheckArgs = @("run", "./cmd/himesan", "check") + $SourcePaths
|
||||
$GenerateArgs = @("run", "./cmd/himesan", "generate") + $SourcePaths
|
||||
Invoke-Checked "generation: read-only freshness check" {
|
||||
Invoke-Checked "golden generation: read-only freshness check" {
|
||||
& go $CheckArgs
|
||||
}
|
||||
$Before = Get-GeneratedManifest
|
||||
|
||||
Invoke-Checked "generation: first deterministic pass" {
|
||||
Invoke-Checked "golden generation: first deterministic pass" {
|
||||
& go $GenerateArgs
|
||||
}
|
||||
$First = Get-GeneratedManifest
|
||||
@@ -110,7 +100,7 @@ try {
|
||||
throw "generation changed committed output after check declared it fresh"
|
||||
}
|
||||
|
||||
Invoke-Checked "generation: second deterministic pass" {
|
||||
Invoke-Checked "golden generation: second deterministic pass" {
|
||||
& go $GenerateArgs
|
||||
}
|
||||
$Second = Get-GeneratedManifest
|
||||
@@ -118,22 +108,11 @@ try {
|
||||
throw "repeated generation changed output bytes or an unchanged timestamp"
|
||||
}
|
||||
|
||||
Invoke-Checked "generation: final freshness check" {
|
||||
Invoke-Checked "golden generation: final freshness check" {
|
||||
& go $CheckArgs
|
||||
}
|
||||
}
|
||||
|
||||
# Do not execute generated application code until check and both generation
|
||||
# passes prove that it is compiler-owned, current, and deterministic.
|
||||
if ($ExampleModules.Count -eq 0) {
|
||||
Write-Host "`n==> examples: no example module exists yet; skipping module tests"
|
||||
}
|
||||
else {
|
||||
foreach ($module in $ExampleModules) {
|
||||
Invoke-ModuleChecks $module.Directory.FullName "example module $($module.Directory.FullName)"
|
||||
}
|
||||
}
|
||||
|
||||
if ($Race) {
|
||||
Invoke-Checked "compiler module: race tests" { go test -race ./... }
|
||||
Push-Location "sando"
|
||||
|
||||
+9
-37
@@ -27,24 +27,11 @@ run_module_checks() {
|
||||
)
|
||||
}
|
||||
|
||||
example_sources() {
|
||||
if [[ ! -d examples ]]; then
|
||||
return 0
|
||||
fi
|
||||
find examples \
|
||||
-type d \( -name .git -o -name vendor \) -prune -o \
|
||||
golden_sources() {
|
||||
find internal/compiler/testdata/golden \
|
||||
-type f -name '*.sando' -print | LC_ALL=C sort
|
||||
}
|
||||
|
||||
example_modules() {
|
||||
if [[ ! -d examples ]]; then
|
||||
return 0
|
||||
fi
|
||||
find examples \
|
||||
-type d \( -name .git -o -name vendor \) -prune -o \
|
||||
-type f -name go.mod -print | LC_ALL=C sort
|
||||
}
|
||||
|
||||
generated_manifest() {
|
||||
local source output digest modified
|
||||
while IFS= read -r source; do
|
||||
@@ -68,7 +55,7 @@ generated_manifest() {
|
||||
modified=$(stat -f '%m' -- "$output")
|
||||
fi
|
||||
printf '%s %s %s\n' "$digest" "$modified" "$output"
|
||||
done < <(example_sources)
|
||||
done < <(golden_sources)
|
||||
}
|
||||
|
||||
log "repository scripts: shell syntax"
|
||||
@@ -92,18 +79,16 @@ if [[ ! -f sando/go.mod ]]; then
|
||||
fi
|
||||
run_module_checks sando "sando runtime module"
|
||||
|
||||
log "static project site"
|
||||
./scripts/check-site.sh
|
||||
|
||||
sources=()
|
||||
while IFS= read -r source; do
|
||||
[[ -n "$source" ]] || continue
|
||||
sources[${#sources[@]}]=$source
|
||||
done < <(example_sources)
|
||||
done < <(golden_sources)
|
||||
if (( ${#sources[@]} == 0 )); then
|
||||
log "generation: no .sando examples exist yet; skipping deterministic-generation check"
|
||||
printf 'error: compiler-owned golden .sando fixture is missing\n' >&2
|
||||
exit 1
|
||||
else
|
||||
log "generation: read-only freshness check"
|
||||
log "golden generation: read-only freshness check"
|
||||
go run ./cmd/himesan check "${sources[@]}"
|
||||
|
||||
manifest_before="$build_dir/generated-before.txt"
|
||||
@@ -111,7 +96,7 @@ else
|
||||
manifest_second="$build_dir/generated-second.txt"
|
||||
generated_manifest >"$manifest_before"
|
||||
|
||||
log "generation: first deterministic pass"
|
||||
log "golden generation: first deterministic pass"
|
||||
go run ./cmd/himesan generate "${sources[@]}"
|
||||
generated_manifest >"$manifest_first"
|
||||
if ! cmp -s "$manifest_before" "$manifest_first"; then
|
||||
@@ -120,7 +105,7 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "generation: second deterministic pass"
|
||||
log "golden generation: second deterministic pass"
|
||||
go run ./cmd/himesan generate "${sources[@]}"
|
||||
generated_manifest >"$manifest_second"
|
||||
if ! cmp -s "$manifest_first" "$manifest_second"; then
|
||||
@@ -132,19 +117,6 @@ else
|
||||
go run ./cmd/himesan check "${sources[@]}"
|
||||
fi
|
||||
|
||||
# Generated application code is not executed until the read-only freshness
|
||||
# check and both deterministic passes prove it is compiler-owned and current.
|
||||
module_count=0
|
||||
while IFS= read -r module_file; do
|
||||
[[ -n "$module_file" ]] || continue
|
||||
module_count=$((module_count + 1))
|
||||
module_dir=$(dirname -- "$module_file")
|
||||
run_module_checks "$module_dir" "example module $module_dir"
|
||||
done < <(example_modules)
|
||||
if (( module_count == 0 )); then
|
||||
log "examples: no example module exists yet; skipping module tests"
|
||||
fi
|
||||
|
||||
if [[ "${HIMESAN_RACE:-0}" == 1 ]]; then
|
||||
log "compiler module: race tests"
|
||||
go test -race ./...
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
# Gamertan site and vanity imports
|
||||
|
||||
This directory is a static, no-JavaScript site for the unsupported Sandwich Hime pre-1.0 public source preview. It is source material only: repository automation must not deploy it. The page is deliberately honest that no supported v1 version exists yet.
|
||||
|
||||
## Intended routes
|
||||
|
||||
The hosting layer must serve these files over HTTPS without an authentication challenge:
|
||||
|
||||
| Request path | File | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `/sandwich-hime` and `/sandwich-hime/` | `index.html` | Project page and compiler-module `go-import` metadata |
|
||||
| `/sandwich-hime/sando` and `/sandwich-hime/sando/` | `sando/index.html` | Nested-runtime metadata using the Go 1.25 `subdirectory` field |
|
||||
| `/sandwich-hime/assets/site.css` | `assets/site.css` | Local-only presentation |
|
||||
|
||||
The Go command requests the exact import path; it does not retry at a parent
|
||||
path after a 404. The hosting layer therefore needs query-scoped metadata
|
||||
fallbacks:
|
||||
|
||||
- any `/sandwich-hime/...` request with `go-get=1`, except the nested runtime
|
||||
subtree, returns the compiler `index.html` with HTTP 200;
|
||||
- `/sandwich-hime/sando` and every path below it with `go-get=1` return
|
||||
`sando/index.html` with HTTP 200;
|
||||
- ordinary browser requests for nonexistent paths continue to return 404.
|
||||
|
||||
This includes `/sandwich-hime/cmd/himesan?go-get=1`, which is the path queried
|
||||
by the documented `go install` command. The `go-import` tags occur early in
|
||||
each document because the Go command uses a restricted HTML parser.
|
||||
|
||||
The nested metadata is intentionally:
|
||||
|
||||
```html
|
||||
<meta name="go-import" content="gamertan.com/sandwich-hime/sando git https://gitea.speelman.ca/gamertan/sandwich-hime.git sando">
|
||||
```
|
||||
|
||||
The fourth field maps the vanity path to the repository’s `sando` subdirectory. It is supported by the project’s minimum Go line, Go 1.25. Runtime versions must use tags such as `sando/v1.0.0`.
|
||||
|
||||
## Hosting setup
|
||||
|
||||
1. Keep the public DNS and TLS authority for `gamertan.com` under founder control. Use an `A`/`AAAA` record or a narrowly scoped `CNAME` appropriate to the chosen static host; do not delegate the whole zone to a project contributor.
|
||||
2. Configure the exact browser routes plus the query-scoped `go-get=1`
|
||||
fallbacks above. Preserve ordinary 404 behavior and do not use a client-side
|
||||
redirect for metadata requests.
|
||||
3. Return `Content-Type: text/html; charset=utf-8` for HTML and `text/css; charset=utf-8` for CSS.
|
||||
4. Add server headers at least equivalent to `Content-Security-Policy: default-src 'none'; style-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'`, `Referrer-Policy: no-referrer`, `X-Content-Type-Options: nosniff`, and a conservative `Permissions-Policy`.
|
||||
5. Keep deployment credentials outside this repository. A future deploy workflow needs a separately reviewed, least-privilege credential and protected environment approval.
|
||||
6. Verify from an uncached public network before announcing installs:
|
||||
|
||||
```sh
|
||||
curl -fsS 'https://gamertan.com/sandwich-hime?go-get=1'
|
||||
curl -fsS 'https://gamertan.com/sandwich-hime/cmd/himesan?go-get=1'
|
||||
curl -fsS 'https://gamertan.com/sandwich-hime/sando?go-get=1'
|
||||
./scripts/verify-public-install.sh --version v1.0.0
|
||||
```
|
||||
|
||||
## Launch blockers
|
||||
|
||||
Do not deploy or remove the pre-release warning until all of these are evidenced:
|
||||
|
||||
- canonical Gitea is public, the security contact works, and protected release-key controls are active;
|
||||
- root `v1.0.0` and nested `sando/v1.0.0` are signed and accompanied by checksums, SBOMs, release binaries, and reproducibility notes;
|
||||
- Go 1.25 and Go 1.26 pass deterministic generation and tests on Linux, macOS, and Windows, including the canonical manual cross-platform workflow;
|
||||
- vulnerability, race, fuzz, adversarial, license, accessibility, and CSP checks pass;
|
||||
- EQL Wiki completes its differential pilot and 14-day production soak without Hime render, security, or accessibility regressions;
|
||||
- the DCO contribution process, license map, governance, output permission, and trademark policy have final human review;
|
||||
- `scripts/release-check.sh --version v1.0.0 --public` passes against a human-reviewed evidence bundle;
|
||||
- the exact public `go install` and `go get` commands pass
|
||||
`scripts/verify-public-install.sh` from clean direct-fetch and public-proxy
|
||||
caches without repository credentials.
|
||||
|
||||
At launch, replace each page’s `himesan-release-status` value with the exact compiler release (for example, `v1.0.0`), replace the human-facing pre-release copy with verified install information, and run `scripts/check-site.sh --public v1.0.0`. The public release preflight enforces that transition so a green evidence bundle cannot accidentally publish a page that still says the runtime does not exist.
|
||||
|
||||
Gitea is the sole public forge. Cross-platform evidence must come from reviewed local or Gitea-runner execution; no secondary mirror or hosted workflow is part of the release plan.
|
||||
@@ -1,398 +0,0 @@
|
||||
/* SPDX-License-Identifier: AGPL-3.0-only */
|
||||
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
--ink: #fff9eb;
|
||||
--muted: #c9c2dd;
|
||||
--night: #100a25;
|
||||
--deep: #251849;
|
||||
--panel: #30235a;
|
||||
--panel-light: #453476;
|
||||
--pink: #ff74b8;
|
||||
--cyan: #70e6e1;
|
||||
--yellow: #ffe38a;
|
||||
--line: #8e75c4;
|
||||
--shadow: #090617;
|
||||
--focus: #fff3a8;
|
||||
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
min-width: 20rem;
|
||||
background: var(--night);
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
color: var(--ink);
|
||||
background-color: var(--night);
|
||||
background-image:
|
||||
linear-gradient(45deg, rgb(112 230 225 / 4%) 25%, transparent 25%),
|
||||
linear-gradient(-45deg, rgb(255 116 184 / 4%) 25%, transparent 25%),
|
||||
linear-gradient(45deg, transparent 75%, rgb(112 230 225 / 4%) 75%),
|
||||
linear-gradient(-45deg, transparent 75%, rgb(255 116 184 / 4%) 75%);
|
||||
background-position: 0 0, 0 8px, 8px -8px, -8px 0;
|
||||
background-size: 16px 16px;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--cyan);
|
||||
text-underline-offset: 0.18em;
|
||||
text-decoration-thickness: 0.1em;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--yellow);
|
||||
}
|
||||
|
||||
a:focus-visible {
|
||||
color: var(--night);
|
||||
background: var(--focus);
|
||||
outline: 0.2rem solid var(--focus);
|
||||
outline-offset: 0.2rem;
|
||||
}
|
||||
|
||||
code,
|
||||
pre {
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
}
|
||||
|
||||
code {
|
||||
color: var(--yellow);
|
||||
}
|
||||
|
||||
.skip-link {
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
top: 0.8rem;
|
||||
left: 0.8rem;
|
||||
padding: 0.65rem 0.9rem;
|
||||
color: var(--night);
|
||||
background: var(--focus);
|
||||
transform: translateY(-180%);
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.masthead {
|
||||
border-bottom: 0.3rem ridge var(--pink);
|
||||
background:
|
||||
radial-gradient(circle at 20% 20%, rgb(112 230 225 / 16%), transparent 28rem),
|
||||
linear-gradient(180deg, var(--deep), var(--night));
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.status-strip {
|
||||
padding: 0.45rem 1rem;
|
||||
color: var(--night);
|
||||
background: repeating-linear-gradient(
|
||||
-45deg,
|
||||
var(--yellow),
|
||||
var(--yellow) 0.7rem,
|
||||
#ffd35a 0.7rem,
|
||||
#ffd35a 1.4rem
|
||||
);
|
||||
border-bottom: 0.15rem solid #6c5414;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.03em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.masthead-inner,
|
||||
main,
|
||||
footer {
|
||||
width: min(70rem, calc(100% - 2rem));
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.masthead-inner {
|
||||
padding: 4rem 0 4.5rem;
|
||||
}
|
||||
|
||||
.compact .masthead-inner {
|
||||
padding-block: 3rem;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
margin: 0 0 0.8rem;
|
||||
color: var(--pink);
|
||||
font-size: 0.82rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.wordmark {
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
margin: 0 auto 1.5rem;
|
||||
overflow: hidden;
|
||||
color: var(--cyan);
|
||||
font-size: clamp(0.72rem, 2.8vw, 1.05rem);
|
||||
font-weight: 700;
|
||||
line-height: 1.15;
|
||||
text-align: left;
|
||||
text-shadow: 0.12rem 0.12rem 0 var(--pink);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
p {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
h1 {
|
||||
max-width: 19ch;
|
||||
margin: 0 auto;
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
font-size: clamp(2.25rem, 7vw, 4.8rem);
|
||||
line-height: 1.02;
|
||||
text-wrap: balance;
|
||||
text-shadow: 0.18rem 0.18rem 0 var(--shadow);
|
||||
}
|
||||
|
||||
.compact h1 {
|
||||
font-size: clamp(2rem, 6vw, 3.8rem);
|
||||
}
|
||||
|
||||
.lede {
|
||||
max-width: 47rem;
|
||||
margin: 1.5rem auto 0;
|
||||
color: var(--muted);
|
||||
font-size: clamp(1.05rem, 2.5vw, 1.28rem);
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 0.8rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.button-row a {
|
||||
padding: 0.7rem 1rem;
|
||||
color: var(--night);
|
||||
background: var(--cyan);
|
||||
border: 0.15rem outset #c6ffff;
|
||||
box-shadow: 0.18rem 0.18rem 0 var(--shadow);
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button-row a:nth-child(2) {
|
||||
background: var(--pink);
|
||||
border-color: #ffc2df;
|
||||
}
|
||||
|
||||
.button-row a:active {
|
||||
border-style: inset;
|
||||
box-shadow: none;
|
||||
transform: translate(0.12rem, 0.12rem);
|
||||
}
|
||||
|
||||
.mirror-note {
|
||||
margin: 1rem auto 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
main {
|
||||
display: grid;
|
||||
gap: 1.5rem;
|
||||
padding-block: 2rem 4rem;
|
||||
}
|
||||
|
||||
.panel {
|
||||
position: relative;
|
||||
padding: clamp(1.25rem, 4vw, 2.5rem);
|
||||
background: linear-gradient(135deg, var(--panel), #271c4a);
|
||||
border: 0.2rem ridge var(--line);
|
||||
box-shadow: 0.35rem 0.35rem 0 var(--shadow);
|
||||
}
|
||||
|
||||
.panel h2 {
|
||||
margin: 0 0 1rem;
|
||||
color: var(--yellow);
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
font-size: clamp(1.55rem, 4vw, 2.35rem);
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.panel h3 {
|
||||
margin-top: 0;
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
.panel p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.code-panel {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 0.8fr) minmax(20rem, 1.2fr);
|
||||
gap: 2rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sticker {
|
||||
display: inline-block;
|
||||
margin: 0 0 1rem;
|
||||
padding: 0.25rem 0.55rem;
|
||||
color: var(--night);
|
||||
background: var(--pink);
|
||||
border: 0.12rem outset #ffd2e8;
|
||||
font: 700 0.72rem/1 Verdana, sans-serif;
|
||||
text-transform: uppercase;
|
||||
transform: rotate(-2deg);
|
||||
}
|
||||
|
||||
.code {
|
||||
margin: 0;
|
||||
padding: 1.25rem;
|
||||
overflow-x: auto;
|
||||
color: var(--ink);
|
||||
background: #0c0919;
|
||||
border: 0.15rem inset var(--line);
|
||||
font-size: clamp(0.78rem, 1.8vw, 0.96rem);
|
||||
line-height: 1.5;
|
||||
tab-size: 2;
|
||||
}
|
||||
|
||||
.code code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.cards article {
|
||||
padding: 1rem;
|
||||
background: rgb(16 10 37 / 55%);
|
||||
border: 0.1rem solid var(--line);
|
||||
}
|
||||
|
||||
.origin {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
gap: 1.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pixel-heart {
|
||||
color: var(--pink);
|
||||
font-family: "Courier New", monospace;
|
||||
font-size: clamp(4rem, 12vw, 7rem);
|
||||
line-height: 1;
|
||||
text-shadow:
|
||||
0.16rem 0 var(--pink),
|
||||
-0.16rem 0 var(--pink),
|
||||
0 0.16rem var(--pink),
|
||||
0.3rem 0.3rem 0 var(--shadow);
|
||||
}
|
||||
|
||||
.checklist {
|
||||
display: grid;
|
||||
gap: 0.55rem;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.checklist span {
|
||||
margin-right: 0.5rem;
|
||||
color: var(--cyan);
|
||||
}
|
||||
|
||||
.honesty-box {
|
||||
padding: 1rem;
|
||||
color: var(--night);
|
||||
background: var(--yellow);
|
||||
border: 0.18rem outset #fff0b9;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.names dl {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.7rem;
|
||||
}
|
||||
|
||||
.names dl div {
|
||||
padding: 0.8rem;
|
||||
background: rgb(16 10 37 / 55%);
|
||||
border-left: 0.3rem solid var(--pink);
|
||||
}
|
||||
|
||||
.names dt {
|
||||
color: var(--cyan);
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.names dd {
|
||||
margin: 0.2rem 0 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding-block: 2rem 3rem;
|
||||
color: var(--muted);
|
||||
border-top: 0.1rem dashed var(--line);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fine-print {
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.code-panel,
|
||||
.cards,
|
||||
.names dl {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.origin {
|
||||
grid-template-columns: 1fr;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
scroll-behavior: auto !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
.panel,
|
||||
.cards article,
|
||||
.names dl div,
|
||||
.button-row a,
|
||||
.honesty-box {
|
||||
border: 0.12rem solid CanvasText;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
-166
@@ -1,166 +0,0 @@
|
||||
<!doctype html>
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="go-import" content="gamertan.com/sandwich-hime git https://gitea.speelman.ca/gamertan/sandwich-hime.git">
|
||||
<meta name="go-source" content="gamertan.com/sandwich-hime https://gitea.speelman.ca/gamertan/sandwich-hime https://gitea.speelman.ca/gamertan/sandwich-hime/src/branch/main{/dir} https://gitea.speelman.ca/gamertan/sandwich-hime/src/branch/main{/dir}/{file}#L{line}">
|
||||
<meta name="himesan-release-status" content="pre-release">
|
||||
<meta name="description" content="Sandwich Hime is an HTML-first, PHP-feeling Go component compiler published as an unsupported pre-1.0 source preview.">
|
||||
<meta name="theme-color" content="#251849">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'self'; img-src 'self'; base-uri 'none'; form-action 'none'">
|
||||
<title>Sandwich Hime — typed Go templates with a hand-built-web heart</title>
|
||||
<link rel="stylesheet" href="./assets/site.css">
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main">Skip to the sandwich</a>
|
||||
|
||||
<header class="masthead">
|
||||
<div class="status-strip" role="status">
|
||||
<span aria-hidden="true">◆</span>
|
||||
Public pre-1.0 source preview — unsupported, no v1 release
|
||||
<span aria-hidden="true">◆</span>
|
||||
</div>
|
||||
<div class="masthead-inner">
|
||||
<p class="eyebrow">Gamertan dot com presents</p>
|
||||
<pre class="wordmark" role="img" aria-label="Sandwich Hime">
|
||||
╭────────────────────────╮
|
||||
│ SANDWICH HIME-SAN │
|
||||
╰──────────┬─────────────╯
|
||||
HTML ♥ GO
|
||||
</pre>
|
||||
<h1>A tiny compiler for people who still want to touch the web.</h1>
|
||||
<p class="lede">
|
||||
Sandwich Hime turns one <code>.sando</code> component into ordinary,
|
||||
reviewable Go. It aims for the immediacy of classic mixed markup,
|
||||
while keeping typed data, contextual safety, and boring deployments.
|
||||
</p>
|
||||
<nav aria-label="Project destinations" class="button-row">
|
||||
<a href="https://gitea.speelman.ca/gamertan/sandwich-hime">Canonical Gitea</a>
|
||||
<a href="#status">Read the release status</a>
|
||||
</nav>
|
||||
<p class="mirror-note">
|
||||
Source and contributions live only on canonical Gitea.
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main">
|
||||
<section class="panel code-panel" aria-labelledby="taste-heading">
|
||||
<div>
|
||||
<p class="sticker" aria-hidden="true">view source!</p>
|
||||
<h2 id="taste-heading">A taste of <code>.sando</code></h2>
|
||||
<p>
|
||||
Markup is the main language. Go appears exactly where the page needs
|
||||
it, and every rendered value is escaped for its known HTML context.
|
||||
</p>
|
||||
</div>
|
||||
<pre class="code" tabindex="0" role="region" aria-label="Profile component template example"><code><?sando go
|
||||
package views
|
||||
|
||||
func Profile(page ProfileView)
|
||||
?>
|
||||
<section class="profile">
|
||||
<h1><?= page.Name ?></h1>
|
||||
<? if page.IsAdmin { ?>
|
||||
<?~ AdminBadge() ?>
|
||||
<? } ?>
|
||||
</section></code></pre>
|
||||
</section>
|
||||
|
||||
<section class="panel" aria-labelledby="boundary-heading">
|
||||
<h2 id="boundary-heading">Compiler at the workbench. Small runtime in production.</h2>
|
||||
<div class="cards">
|
||||
<article>
|
||||
<h3><code>himesan</code></h3>
|
||||
<p>
|
||||
A globally installed development tool that checks, generates, and
|
||||
supervises a local server. It does not own your router or app.
|
||||
</p>
|
||||
</article>
|
||||
<article>
|
||||
<h3><code>page.sando.go</code></h3>
|
||||
<p>
|
||||
Deterministic generated Go, committed beside its source so code
|
||||
review and production builds do not need the compiler.
|
||||
</p>
|
||||
</article>
|
||||
<article>
|
||||
<h3><code>sando</code></h3>
|
||||
<p>
|
||||
A deliberately small Apache-2.0 runtime: components, error-aware
|
||||
rendering, and stable escaping helpers—no production server.
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel origin" aria-labelledby="origin-heading">
|
||||
<div class="pixel-heart" aria-hidden="true">♥</div>
|
||||
<div>
|
||||
<h2 id="origin-heading">From a 2004 PSO game-club page, with love</h2>
|
||||
<p>
|
||||
The spirit is personal-site webmaking: open the file, understand the
|
||||
page, change the page. Sandwich Hime is a love letter to a first
|
||||
Geocities-era site last updated in 2004—rebuilt with the safety and
|
||||
repeatability expected from a serious modern Go project.
|
||||
</p>
|
||||
<p>
|
||||
The checkerboard, bevels, and tiny status lights are intentional.
|
||||
The security model is also intentional.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel" id="status" aria-labelledby="status-heading">
|
||||
<h2 id="status-heading">Proof first. Release second.</h2>
|
||||
<p>
|
||||
Hime-san has no supported tagged public version yet. EQL Wiki is the
|
||||
production proving ground, and v1 remains unreleased until the compiler,
|
||||
runtime, development supervisor, and contextual escaping have durable
|
||||
evidence behind them.
|
||||
</p>
|
||||
<ul class="checklist">
|
||||
<li><span aria-hidden="true">◌</span> Cross-platform deterministic generation on Go 1.25 and 1.26</li>
|
||||
<li><span aria-hidden="true">◌</span> Adversarial, fuzz, race, vulnerability, and accessibility gates</li>
|
||||
<li><span aria-hidden="true">◌</span> EQL differential pilot and a clean 14-day production soak</li>
|
||||
<li><span aria-hidden="true">◌</span> Working security contact and human-reviewed project policies</li>
|
||||
<li><span aria-hidden="true">◌</span> Signed tags, checksums, SBOMs, binaries, and vanity paths</li>
|
||||
</ul>
|
||||
<p class="honesty-box">
|
||||
No “fastest template engine” promises live here. Any performance claim
|
||||
will ship with the hardware, commit, dataset, commands, latency, and
|
||||
allocation results needed to reproduce it.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="panel names" aria-labelledby="names-heading">
|
||||
<h2 id="names-heading">Please keep the name tags on the right guests</h2>
|
||||
<dl>
|
||||
<div><dt>Project</dt><dd>Sandwich Hime / Hime-san</dd></div>
|
||||
<div><dt>Command</dt><dd><code>himesan</code></dd></div>
|
||||
<div><dt>Template</dt><dd><code>page.sando</code></dd></div>
|
||||
<div><dt>Runtime</dt><dd><code>sando</code></dd></div>
|
||||
<div><dt>San language</dt><dd><code>.san</code>—reserved, separate, and never a Hime-san template</dd></div>
|
||||
</dl>
|
||||
<p>
|
||||
“Hime” on its own already belongs to an unrelated Go framework. The
|
||||
compound Sandwich Hime and Hime-san identity avoids stepping on it.
|
||||
</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>
|
||||
<span aria-hidden="true">[</span>
|
||||
Hand-built for the web by Gamertan
|
||||
<span aria-hidden="true">]</span>
|
||||
</p>
|
||||
<p class="fine-print">
|
||||
Unsupported pre-1.0 preview. Compiler sources are AGPL-3.0-only; the
|
||||
nested runtime is Apache-2.0; copyable examples are 0BSD.
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,47 +0,0 @@
|
||||
<!doctype html>
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="go-import" content="gamertan.com/sandwich-hime/sando git https://gitea.speelman.ca/gamertan/sandwich-hime.git sando">
|
||||
<meta name="go-source" content="gamertan.com/sandwich-hime/sando https://gitea.speelman.ca/gamertan/sandwich-hime/src/branch/main/sando https://gitea.speelman.ca/gamertan/sandwich-hime/src/branch/main/sando{/dir} https://gitea.speelman.ca/gamertan/sandwich-hime/src/branch/main/sando{/dir}/{file}#L{line}">
|
||||
<meta name="himesan-release-status" content="pre-release">
|
||||
<meta name="description" content="Vanity import metadata for the Apache-2.0 Sandwich Hime sando runtime.">
|
||||
<meta name="theme-color" content="#251849">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'self'; base-uri 'none'; form-action 'none'">
|
||||
<title>sando runtime — Sandwich Hime</title>
|
||||
<link rel="stylesheet" href="../assets/site.css">
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main">Skip to the runtime note</a>
|
||||
<header class="masthead compact">
|
||||
<div class="status-strip" role="status">Public source-preview metadata — no public runtime tag yet</div>
|
||||
<div class="masthead-inner">
|
||||
<p class="eyebrow">Sandwich Hime / Hime-san</p>
|
||||
<h1><code>sando</code> is the small production runtime.</h1>
|
||||
<p class="lede">
|
||||
This page carries Go 1.25+ vanity-import metadata for the nested module
|
||||
at <code>gamertan.com/sandwich-hime/sando</code>.
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
<main id="main">
|
||||
<section class="panel">
|
||||
<h2>Not released yet</h2>
|
||||
<p>
|
||||
The runtime will be tagged independently as <code>sando/vX.Y.Z</code>
|
||||
and licensed Apache-2.0. Do not add this vanity path to production until
|
||||
a signed release exists and the install check is published.
|
||||
</p>
|
||||
<p>
|
||||
<a href="../">Return to the Sandwich Hime project page</a> or inspect
|
||||
the <a href="https://gitea.speelman.ca/gamertan/sandwich-hime/src/branch/main/sando">canonical runtime source on Gitea</a>.
|
||||
</p>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<p>HTML-first templates. Ordinary Go. A hand-built-web heart.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user