Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59d21ce33f | ||
|
|
cdfed23c69 | ||
|
|
078835e734 | ||
|
|
5d57fc551f | ||
|
|
32b8b39f95 | ||
|
|
7cc3642b40 | ||
|
|
9c687b00b9 | ||
|
|
9242fc7d25 | ||
|
|
73b104f7f2 | ||
|
|
05444c1438 |
No files matched your search
@@ -1,6 +0,0 @@
|
||||
.git
|
||||
.delta
|
||||
.idea
|
||||
.vscode
|
||||
docs
|
||||
legacy_docs
|
||||
@@ -1,17 +0,0 @@
|
||||
# Server configuration. Copy to .env (gitignored), export or source it.
|
||||
|
||||
# Address the server listens on.
|
||||
LISTEN_ADDR=:8080
|
||||
# Session lifetime.
|
||||
SESSION_TTL=12h
|
||||
# Set to false only when testing locally over plain http.
|
||||
COOKIE_SECURE=false
|
||||
|
||||
# OIDC (Authentik). Unset: public pages still work, protected pages answer 503.
|
||||
OIDC_ISSUER_URL=https://auth.example.com/application/o/yggdrasil/
|
||||
OIDC_CLIENT_ID=yggdrasil
|
||||
OIDC_CLIENT_SECRET=change-me
|
||||
OIDC_REDIRECT_URL=http://localhost:8080/callback
|
||||
# Comma/space separated scopes. The groups claim must be covered by a scope
|
||||
# mapping on the provider for group checks to work.
|
||||
# OIDC_SCOPES=openid profile email groups
|
||||
@@ -1,48 +0,0 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
# shiroa bundles its own Typst compiler, so no separate typst install.
|
||||
- name: Install shiroa
|
||||
run: |
|
||||
curl -sSL -o /tmp/shiroa.tar.gz \
|
||||
https://github.com/Myriad-Dreamin/shiroa/releases/download/v0.4.0-rc2/shiroa-x86_64-unknown-linux-musl.tar.gz
|
||||
tar -xzf /tmp/shiroa.tar.gz -C /tmp
|
||||
install -m 755 /tmp/shiroa-x86_64-unknown-linux-musl/shiroa /usr/local/bin/shiroa
|
||||
shiroa --version
|
||||
|
||||
- name: Generate acl.json
|
||||
run: go run ./cmd/parser -in docs -out acl.json
|
||||
|
||||
- name: Build the book
|
||||
run: shiroa build docs --dest-dir ../dist
|
||||
|
||||
- name: Test and build the server
|
||||
run: |
|
||||
go vet ./...
|
||||
go test ./...
|
||||
test -z "$(gofmt -l cmd internal embed.go)" || { echo 'gofmt needed:'; gofmt -l cmd internal embed.go; exit 1; }
|
||||
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o yggdrasil-server ./cmd/server
|
||||
|
||||
# upload-artifact v4 needs a reasonably current Gitea/act_runner;
|
||||
# fall back to v3 if your runner is too old to serve v4 artifacts.
|
||||
- name: Upload server artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: yggdrasil-server-linux-amd64
|
||||
path: yggdrasil-server
|
||||
@@ -1,21 +0,0 @@
|
||||
# build outputs (dist is regenerated by shiroa; .gitkeep keeps go:embed happy)
|
||||
/dist/*
|
||||
!/dist/.gitkeep
|
||||
/server
|
||||
yggdrasil-server
|
||||
|
||||
# secrets
|
||||
.env
|
||||
|
||||
# editors
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# delta workspace
|
||||
/.delta/
|
||||
|
||||
# pinned toolchain downloads (shiroa), managed by the Justfile
|
||||
tools/
|
||||
|
||||
# shiroa serve default output
|
||||
docs/dist/
|
||||
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Docsify" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="start http://localhost:3000 ; docsify serve " />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="false" />
|
||||
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/.idea/runConfigurations/run.sh" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="powershell.exe" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,80 +0,0 @@
|
||||
# AGENT.md — operating rules for agents working on Yggdrasil
|
||||
|
||||
Personal knowledge base: Typst sources (`docs/`, a shiroa book) compiled to
|
||||
a static site, embedded into a Go binary that enforces per-page OIDC group
|
||||
permissions (Authentik). README.md is the human-facing documentation; this
|
||||
file is the working rulebook. Follow it; when it conflicts with the user,
|
||||
the user wins.
|
||||
|
||||
## Commands
|
||||
|
||||
- `just` — shows the command overview (bare `just` never builds anything).
|
||||
- `just build` — parse ACL, compile the book, build `./server`.
|
||||
- `just run` — build everything, then serve on :8080.
|
||||
- `just check` — go vet, go test, gofmt. Run it before every commit.
|
||||
- `just clean` — removes `dist/` and `server` (keeps `tools/` and
|
||||
restores the committed `dist/.gitkeep`).
|
||||
- The shiroa toolchain is pinned and self-bootstrapped into `tools/` by the
|
||||
`tools` recipe. Never assume a global shiroa/typst install exists; do not
|
||||
install shiroa globally; do not bump the pin casually (see Traps).
|
||||
|
||||
## Layout
|
||||
|
||||
- `docs/` — shiroa book. `book.typ` lists every built page; files not listed
|
||||
there are not part of the site. Categories are `=` part headers in the
|
||||
summary, pages are `# chapter("path.typ")[Title]` entries. Final URL =
|
||||
source path with `.typ` replaced by `.html`.
|
||||
- `cmd/parser` — build step: scans `docs/` for `#let access-groups = (..)`
|
||||
tags and emits `acl.json`. Untagged pages are public.
|
||||
- `cmd/server` + `embed.go` (repo root) — `//go:embed` of `dist/` and
|
||||
`acl.json` into a single binary.
|
||||
- `internal/acl` — ACL loading and page-key normalization (security critical).
|
||||
- `internal/oidc` — OIDC authorization-code client.
|
||||
- `internal/middleware` — sessions, login/callback/logout, access gate.
|
||||
- `legacy_docs/` — frozen previous site. Never modify or migrate it unless
|
||||
the user asks explicitly.
|
||||
|
||||
## Security invariants — do not break
|
||||
|
||||
- Fail closed: a malformed `acl.json` aborts startup; protected pages with
|
||||
no configured IdP answer 503, never the content.
|
||||
- Page keys: shiroa pages keep their rendered content in sibling
|
||||
`<page>.<theme>.multi.sir.in` files fetched by client-side JS.
|
||||
`acl.PageKey` reduces any URL to a page key so those files inherit the
|
||||
page's protection. If the shiroa output format changes, re-verify this
|
||||
mapping: content in any new sidecar file must be covered or blocked.
|
||||
- `/searchindex.json` contains the text of protected pages: blocked at the
|
||||
router, always.
|
||||
- Session cookies: HttpOnly, SameSite=Lax, Secure unless COOKIE_SECURE=false.
|
||||
OAuth2 states are random and single use; post-login redirect targets are
|
||||
sanitized (open-redirect guard).
|
||||
|
||||
## Traps
|
||||
|
||||
- Shiroa version pairing: the CLI and the `@preview/shiroa:*` typst package
|
||||
must match (CLI v0.4.0-rc2 pairs with package 0.4.0; the v0.3.0 release
|
||||
CLI rejects 0.4.0 books). The pin lives in two places: the Justfile
|
||||
`shiroa-version` and `.gitea/workflows/build.yml`. Bump all of them
|
||||
together with the package import and rebuild.
|
||||
- Typst call syntax: no space between `)` and the trailing content block —
|
||||
`#chapter("x.typ")[Title]`, not `#chapter("x.typ") [Title]`.
|
||||
- `dist/` and `acl.json` are embedded at Go build time; there is no runtime
|
||||
reload. After changing docs or ACL, rebuild the binary.
|
||||
- `go build` needs `dist/` and `acl.json` to exist: fresh clones carry
|
||||
`dist/.gitkeep` and a committed `acl.json` for exactly this reason.
|
||||
Never gitignore those two.
|
||||
|
||||
## Testing expectations
|
||||
|
||||
- Test what the user runs, not a proxy for it: from a clean state
|
||||
(`rm -rf tools && just clean`), `just build` and `just run` must succeed
|
||||
with no global shiroa on PATH. Test the bare `just` output too. After
|
||||
any clean/test cycle, `git status` must be clean: `dist/.gitkeep` is
|
||||
tracked, and committing a worktree where it is missing breaks fresh
|
||||
clones (`go:embed` needs `dist/`).
|
||||
- Server behavior: curl a public page (200), a protected page (503 without
|
||||
OIDC env; it must never serve protected content), and `/searchindex.json`
|
||||
(404). Extend the unit tests in `internal/acl` and `internal/middleware`
|
||||
whenever touching those paths.
|
||||
- A live OIDC round trip needs a real IdP and is not testable here. Say
|
||||
that explicitly when reporting what was verified.
|
||||
@@ -1,16 +0,0 @@
|
||||
# Rootless image of the Yggdrasil server.
|
||||
# Build after `just parse build-docs` so that dist/ and acl.json are current:
|
||||
# just parse build-docs && docker build -t yggdrasil .
|
||||
|
||||
FROM golang:1.27 AS build
|
||||
WORKDIR /src
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/server ./cmd/server
|
||||
|
||||
FROM gcr.io/distroless/static-debian12:nonroot
|
||||
COPY --from=build /out/server /server
|
||||
EXPOSE 8080
|
||||
USER nonroot:nonroot
|
||||
ENTRYPOINT ["/server"]
|
||||
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
@@ -1,52 +0,0 @@
|
||||
# Yggdrasil: Typst documentation behind an authenticated Go server.
|
||||
|
||||
# Pinned shiroa toolchain, self-managed under tools/ (no global install).
|
||||
# The CLI and the @preview/shiroa package version must match (see AGENT.md).
|
||||
shiroa-version := "v0.4.0-rc2"
|
||||
shiroa-target := if arch() == "x86_64" { "x86_64-unknown-linux-musl" } else if arch() == "aarch64" { "aarch64-unknown-linux-musl" } else { error("unsupported architecture " + arch()) }
|
||||
shiroa-dir := "tools/shiroa"
|
||||
shiroa-bin := shiroa-dir + "/shiroa-" + shiroa-target + "/shiroa"
|
||||
shiroa-url := "https://github.com/Myriad-Dreamin/shiroa/releases/download/" + shiroa-version + "/shiroa-" + shiroa-target + ".tar.gz"
|
||||
|
||||
# Show the available commands.
|
||||
default:
|
||||
@just --list
|
||||
|
||||
# Build everything: acl.json, the static site, and the server binary.
|
||||
build: parse build-docs build-server
|
||||
|
||||
# Scan docs/ for access-groups tags and regenerate acl.json.
|
||||
parse:
|
||||
go run ./cmd/parser -in docs -out acl.json
|
||||
|
||||
# Download the pinned shiroa into tools/ if missing.
|
||||
tools:
|
||||
@mkdir -p {{shiroa-dir}}
|
||||
@test -x {{shiroa-bin}} || { echo "Downloading shiroa {{shiroa-version}} ({{shiroa-target}})..."; curl -fsSL -o {{shiroa-dir}}/shiroa.tar.gz {{shiroa-url}}; tar -xzf {{shiroa-dir}}/shiroa.tar.gz -C {{shiroa-dir}}; rm {{shiroa-dir}}/shiroa.tar.gz; {{shiroa-bin}} --version; }
|
||||
|
||||
# Compile the Typst book into dist/.
|
||||
# (--dest-dir is resolved relative to the book directory, docs/, hence ../dist)
|
||||
build-docs: tools
|
||||
{{shiroa-bin}} build docs --dest-dir ../dist
|
||||
|
||||
# Build the server binary (embeds dist/ and acl.json).
|
||||
build-server:
|
||||
CGO_ENABLED=0 go build -trimpath -o server ./cmd/server
|
||||
|
||||
# Rebuild everything and run the server locally.
|
||||
run: build
|
||||
./server
|
||||
|
||||
# Live-preview the book without authentication (shiroa dev server).
|
||||
preview-docs: tools
|
||||
{{shiroa-bin}} serve docs
|
||||
|
||||
# Run static checks and tests.
|
||||
check:
|
||||
go vet ./...
|
||||
go test ./...
|
||||
@test -z "$(gofmt -l cmd internal embed.go)" || { echo 'gofmt needed:'; gofmt -l cmd internal embed.go; exit 1; }
|
||||
|
||||
# Remove build artifacts (keeps tools/ and the committed dist/.gitkeep).
|
||||
clean:
|
||||
rm -rf dist server && mkdir -p dist && touch dist/.gitkeep
|
||||
@@ -1,200 +1,27 @@
|
||||
# Yggdrasil
|
||||
<header>
|
||||
<h1>Yggdrasil</h1>
|
||||
</header>
|
||||
Yggdrasil is a collections of notes around IT, OT and other geeks stuff
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
A personal knowledge base written in [Typst](https://typst.app), compiled to
|
||||
a static site by [shiroa](https://github.com/Myriad-Dreamin/shiroa), and
|
||||
served by a small Go binary that embeds the whole site and gates protected
|
||||
pages behind OIDC login (Authentik) with per-page group permissions.
|
||||
# [Bachelor in embedded system](/infotro/)
|
||||
Here notes I take during my bachelor in embedded systems
|
||||
|
||||
No search, no database, no Node.js: one binary, one configuration.
|
||||
Mainly some topics:
|
||||
- [PTR - Programmation Temps Réel](/infotro/PTR/)
|
||||
- [SDi - Système Distribué](/infotro/SDi/)
|
||||
- [Eln - Électronique](/infotro/Eln/)
|
||||
- [SEm - Systèmes Embarqué](/infotro/SEm/)
|
||||
|
||||
## How it works
|
||||
# [Embedded Systems](/Embed/)
|
||||
Here some note specific about some Embedded System
|
||||
- [STM32](/Embed/STM32/)
|
||||
- [nRF](/Embed/nRF/)
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["docs/*.typ (Typst sources)"] -->|"just build-docs (shiroa)"| B["dist/ (static site)"]
|
||||
A -->|"just parse (cmd/parser, access-groups tags)"| C["acl.json"]
|
||||
B -->|"go build (//go:embed)"| E["server binary"]
|
||||
C --> E
|
||||
E --> F{"request /page.html"}
|
||||
F -->|"public page"| G["serve page"]
|
||||
F -->|"protected page"| H{"session + groups ok?"}
|
||||
H -->|"no session"| I["redirect to /login, then Authentik"]
|
||||
H -->|"not authorized"| J["403"]
|
||||
H -->|"authorized"| G
|
||||
```
|
||||
# [Working environment at HES-SO](/HES-SO/)
|
||||
Here some note about the working environment at HES-SO
|
||||
|
||||
- Pages are written in Typst under `docs/` and listed in `docs/book.typ`.
|
||||
- A page can declare who may read it with a tag:
|
||||
`#let access-groups = ("family", "admin")`. Pages without a tag are public.
|
||||
- At build time, `cmd/parser` scans `docs/`, extracts the tags and generates
|
||||
`acl.json` mapping final page URLs to allowed groups.
|
||||
- The Go server embeds both `dist/` and `acl.json`. For every request it
|
||||
reduces the URL to a *page key* and looks it up in the ACL: public pages
|
||||
are served directly; protected pages require a session whose groups include
|
||||
at least one of the allowed groups.
|
||||
- Users authenticate with OIDC. The server reads the `groups` claim of the
|
||||
verified ID token to build the session.
|
||||
|
||||
## Repository layout
|
||||
|
||||
```
|
||||
docs/ Typst sources (shiroa book)
|
||||
book.typ book metadata + chapter list (the sidebar)
|
||||
template.typ page layout (from the shiroa minimal template)
|
||||
index.typ home page (public)
|
||||
thermo/, security/, homelab/ example categories
|
||||
cmd/parser/ generates acl.json from access-groups tags
|
||||
cmd/server/ the web server
|
||||
internal/acl/ ACL loading, page-key normalization, group checks
|
||||
internal/oidc/ OIDC client (authorization code flow)
|
||||
internal/middleware/ sessions, login/logout/callback, access gate
|
||||
dist/ compiled site (generated, embedded at build time)
|
||||
tools/ pinned shiroa binary (downloaded by `just tools`)
|
||||
acl.json generated ACL (embedded at build time)
|
||||
legacy_docs/ the old documentation base (kept for manual migration)
|
||||
.gitea/workflows/ Gitea Actions CI
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
Prerequisites: Go 1.27.1+ (the version required by `go.mod`) and
|
||||
[just](https://github.com/casey/just). The
|
||||
pinned shiroa toolchain is downloaded automatically into `tools/` on the
|
||||
first build (needs `curl` + `tar` and network access); nothing is
|
||||
installed globally.
|
||||
|
||||
```sh
|
||||
just # show the available commands
|
||||
just run # parse ACL, compile the book, build and start the server
|
||||
```
|
||||
|
||||
Then open http://localhost:8080. The home page and public pages render;
|
||||
protected pages (`/thermo/fluids.html`, `/security/ssh.html`,
|
||||
`/homelab/backup.html` in the example book) return **503** until OIDC is
|
||||
configured.
|
||||
|
||||
For pure writing previews without authentication, use `just preview-docs`.
|
||||
|
||||
## Writing documentation
|
||||
|
||||
The book structure mirrors the URL structure: `docs/<path>.typ` is compiled
|
||||
to `/<path>.html`. To add a page:
|
||||
|
||||
1. Create the file, e.g. `docs/security/wireguard.typ`, starting with:
|
||||
|
||||
```typst
|
||||
#import "../template.typ": project
|
||||
#show: project
|
||||
|
||||
= WireGuard
|
||||
|
||||
Your content...
|
||||
```
|
||||
|
||||
2. Reference it in `docs/book.typ`. A `=` heading creates a category
|
||||
(part) in the sidebar; each `- #chapter(...)` is a page:
|
||||
|
||||
```typst
|
||||
= Security
|
||||
- #chapter("security/ssh.typ", section: "4")[SSH hardening]
|
||||
- #chapter("security/wireguard.typ", section: "5")[WireGuard]
|
||||
```
|
||||
|
||||
3. Only pages listed in `book.typ` are built. Keep `section:` numbers
|
||||
consistent (sub-chapters like `"4.1"` nest under `"4"`).
|
||||
|
||||
To restrict a page, add the tag on the first line of its file:
|
||||
|
||||
```typst
|
||||
// Only readable by members of these groups.
|
||||
#let access-groups = ("family", "admin")
|
||||
```
|
||||
|
||||
- The groups are compared against the `groups` claim of the OIDC ID token
|
||||
(in Authentik: your user's group memberships).
|
||||
- A page without the tag is public.
|
||||
- Everything served *for* a protected page is protected too: the shiroa
|
||||
renderer fetches page content from `*.multi.sir.in` files, and those
|
||||
reduce to the same ACL entry as the page itself.
|
||||
- The search index (`searchindex.json`) would also contain protected text,
|
||||
so the server never serves it.
|
||||
|
||||
## Configuration
|
||||
|
||||
Environment variables (see `.env.example`):
|
||||
|
||||
| Variable | Default | Meaning |
|
||||
|---------------------|-----------------------------|---------|
|
||||
| `LISTEN_ADDR` | `:8080` | listen address |
|
||||
| `SESSION_TTL` | `12h` | session lifetime |
|
||||
| `COOKIE_SECURE` | `true` | `Secure` cookie flag; use `false` only on plain-http local tests |
|
||||
| `OIDC_ISSUER_URL` | (unset) | e.g. `https://auth.example.com/application/o/yggdrasil/` |
|
||||
| `OIDC_CLIENT_ID` | (unset) | OAuth2 client id |
|
||||
| `OIDC_CLIENT_SECRET`| (unset) | OAuth2 client secret |
|
||||
| `OIDC_REDIRECT_URL` | (unset) | e.g. `https://docs.example.com/callback` |
|
||||
| `OIDC_SCOPES` | `openid profile email groups` | requested scopes |
|
||||
|
||||
Without the `OIDC_*` variables the server runs in public-only mode:
|
||||
public pages are served, protected pages answer 503. The server refuses to
|
||||
start on a malformed `acl.json` or an unreachable OIDC issuer (fail closed).
|
||||
|
||||
### Setting up Authentik
|
||||
|
||||
1. **Applications -> Providers -> Create**: type *OAuth2/OpenID Connect*.
|
||||
2. Authorization flow: *implicit/explicit consent* (default is fine).
|
||||
3. Redirect URI: exactly `https://your-docs-host/callback`.
|
||||
4. Client type: *Confidential*; note the client id and secret.
|
||||
5. **Scope mappings**: make sure the issued ID token contains a `groups`
|
||||
claim. Recent Authentik versions ship a scope mapping that adds
|
||||
`groups`; otherwise create a *Property Mapping (Scope)* with e.g.
|
||||
`return {"groups": [g.name for g in user.ak_groups.all()]}` and add it
|
||||
to the provider.
|
||||
6. Note the issuer URL shown on the provider page
|
||||
(`.../application/o/<slug>/`) and set `OIDC_ISSUER_URL` to it.
|
||||
7. Create groups in **Directory -> Groups** (e.g. `family`, `admin`) whose
|
||||
names match the `access-groups` in your pages, and add users to them.
|
||||
|
||||
The redirect after login returns the user to the page they originally
|
||||
requested. Logout is available at `/logout`.
|
||||
|
||||
## Build and CI
|
||||
|
||||
Local commands (see `Justfile`): `just build`, `just run`, `just parse`,
|
||||
`just check`, `just clean`, `just preview-docs`. Bare `just` prints the
|
||||
command overview. The pinned shiroa lives in `tools/` (gitignored) and is
|
||||
downloaded on demand.
|
||||
|
||||
`.gitea/workflows/build.yml` (Gitea Actions, not GitLab CI) runs on push to
|
||||
`main` and on pull requests: installs shiroa, regenerates `acl.json`,
|
||||
compiles the book, runs `go vet`/`go test`, builds the static binary and
|
||||
uploads it as an artifact.
|
||||
|
||||
A rootless Docker/Podman image is available:
|
||||
|
||||
```sh
|
||||
just parse build-docs
|
||||
docker build -t yggdrasil .
|
||||
docker run --rm -p 8080:8080 --env-file .env yggdrasil
|
||||
```
|
||||
|
||||
Note that the binary embeds the site at build time: publishing new or
|
||||
edited pages always requires rebuilding and redeploying the server.
|
||||
|
||||
## Security notes and limitations
|
||||
|
||||
- Sessions live in memory: they vanish on restart and do not scale past
|
||||
one instance (users simply log in again).
|
||||
- Page *titles* and URLs of protected pages are visible in the sidebar of
|
||||
public pages (that is inherent to a static site). Page *content* is not:
|
||||
neither the HTML nor the renderer data files can be fetched without
|
||||
authorization.
|
||||
- Directory listing is enabled (file names only, e.g. `/thermo/`).
|
||||
- Logout is a GET request (`/logout`), like most documentation sites.
|
||||
- No search is implemented (`searchindex.json` is explicitly blocked).
|
||||
- Static assets referenced by pages (theme CSS/JS, fonts, renderer wasm)
|
||||
are public; they contain no page content.
|
||||
|
||||
## Legacy documentation
|
||||
|
||||
The previous documentation base lives in `legacy_docs/` and is kept only
|
||||
for reference; migrate pages into `docs/` manually, then delete the folder.
|
||||
- [OneDrive - Linux](/HES-SO/OneDrive.md)
|
||||
- [Teams - Linux](/HES-SO/Teams.md)
|
||||
|
After Width: | Height: | Size: 70 KiB |
@@ -0,0 +1,13 @@
|
||||
# 5G
|
||||
La 5G est l'évolution de le 4G LTE.
|
||||
|
||||
|
||||
## Fonctionnement
|
||||
|
||||
### 5G Standalone VS non-standalone
|
||||

|
||||
|
||||
|
||||
# Liens
|
||||
- [5G NR explications](https://ieeexplore.ieee.org/abstract/document/8879484https://ieeexplore.ieee.org/abstract/document/8879484)
|
||||
- [5G RAN](https://ieeexplore.ieee.org/document/9844937)
|
||||
@@ -0,0 +1,23 @@
|
||||
# C-V2X vs ITS-G5
|
||||
|
||||
# IEEE 802.11p
|
||||
Physical layer :
|
||||
|
||||
Il utilise l'[OFDM](/Wireless-Communication/Protocol/glossary.md#ofdm) pour varier de channel avec deux paramètres : le temps doublé qui implique
|
||||
que la fréquence est divisée de moitié.
|
||||
|
||||
Mac layer :
|
||||
|
||||
Il est basé sur un #OCB operation mode, où on utlisie pas les service d'authentification, d'association et de confidentialité de donnée.
|
||||
Mais, il est bien fait pour émettre de cours messages en broadcast et un au niveau de mobilité.
|
||||
|
||||
# C-V2X
|
||||

|
||||
|
||||
utilise du QPSK ou du 16-QAM
|
||||
|
||||
|
||||
# Conclusion
|
||||
Le C-V2X a de meilleure performance avec le même data rate que le [ITS-G5](/Wireless-Communication/Protocol/glossary.md#its-5g), il possède
|
||||
d'autres paramètres pouvant influencer positivement ses performances. Mais, dès qu'on augmente la densité d'utilisateurs et est supérieur à
|
||||
150 users/km2, le [ITS-G5](/Wireless-Communication/Protocol/glossary.md#its-5g) devient meilleur.
|
||||
@@ -0,0 +1 @@
|
||||
# DECT NR+
|
||||
@@ -0,0 +1,28 @@
|
||||
# IEEE 802.11
|
||||
[IEEE working group](https://www.ieee802.org/11/)
|
||||
|
||||
## 802.11p / ITS-G5
|
||||
|
||||
Cette technologie est la première standardisée pour la communication impliquant des véhicules.
|
||||
|
||||
##* Fonctionnement
|
||||
Elle utilise l'[OFDM](/Wireless-Communication/Protocol/glossary.md#ofdm) :
|
||||
|
||||
Channel Bandwith : 10MHz
|
||||
Modulation : 8
|
||||
|
||||
BitRate : 27Mb/s
|
||||
|
||||
[SCS](/Wireless-Communication/Protocol/glossary.md#scs) : 156.25 kHz
|
||||
|
||||
[CSMA/CA](/Wireless-Communication/Protocol/glossary.md#csmaca)
|
||||
|
||||
Designer pour peu de latence et une grande fiabilité, expressement pour une utilisation sécurisée
|
||||
sur route.
|
||||
|
||||
## IEEE 802.11BD / DOT11BD
|
||||
|
||||
|
||||
|
||||
liens :
|
||||
- [802.11p -> ITS-G5](https://ieeexplore.ieee.org/document/9734746)
|
||||
|
After Width: | Height: | Size: 342 KiB |
@@ -0,0 +1,11 @@
|
||||
<header><h1>
|
||||
Protocol de communication wireless
|
||||
</h1></header>
|
||||
|
||||
- [Glossaire](/Wireless-Communication/Protocol/glossary)
|
||||
- [5G](/Wireless-Communication/Protocol/5g)
|
||||
- [C-V2X vs ITS-G5](/Wireless-Communication/Protocol/C-V2X-vs-ITS-G5)
|
||||
- [IEEE 802.11](/Wireless-Communication/Protocol/IEEE%20802.11)
|
||||
- [V2X](/Wireless-Communication/Protocol/V2X)
|
||||
- [DECT NR+](/Wireless-Communication/Protocol/DECT-NR-+.md)
|
||||
- [Visible light communication](/Wireless-Communication/Protocol/Visible-light-communication.md)
|
||||
@@ -0,0 +1,41 @@
|
||||
# V2X
|
||||
Cette technologie utilise le concept de Sidelink
|
||||
|
||||
## C-V2X
|
||||
|
||||
### LTE-V2X
|
||||
Il est basé sur l'interface sideLink PC5 et la communication cellulaire.
|
||||
|
||||
Il support le broadcast, le groupcast et le unicast.
|
||||
|
||||
#### Mode 3
|
||||
Les [eNB](/Wireless-Communication/Protocol/glossary.md#enb) réservent et planifient les ressources
|
||||
radio pour les véhicules.
|
||||
|
||||
#### Mode 4
|
||||
Les véhicules réservent et planifient les ressource qu'ils ont besoin de manière autonome.
|
||||
|
||||
#### Conclusion
|
||||
Le mode 3 va dépendre de du réseau cellulaire, alors que le 4 est autonome.
|
||||
|
||||
### NR-V2X/5G-V2X
|
||||
|
||||
Il supporte le broadcast, le groupcast et le unicast.
|
||||
|
||||
Il permet d'avoir des sous fréquences porteuses en multiple de 15kHz qui favorise les transmissons
|
||||
critiques.
|
||||
|
||||
#### Mode 1
|
||||
|
||||
#### Mode 2
|
||||
|
||||
### Conclusion
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Liens
|
||||
|
||||
- [LTE/NR-V2X](https://ieeexplore.ieee.org/document/9734746)
|
||||
@@ -0,0 +1,10 @@
|
||||
# Visible light communication
|
||||
|
||||
La technologie utilise de la lumière visible pour transmettre une information.
|
||||
|
||||
Il y a une application pour véhicule, mais elle s'applique particulièrement bien pour le voiture qui possèdent déjà de
|
||||
phares. Donc, je ne vois pas l'utilité de ce système pour les trains.
|
||||
|
||||
# Liens :
|
||||
- [applications](https://www.sciencedirect.com/science/article/pii/S2352864816300335)
|
||||
-
|
||||
@@ -0,0 +1,45 @@
|
||||
# Global glossary
|
||||
## V2X
|
||||
standard vehicle to vehicle/infrastructure communication systems from 3GPP
|
||||
|
||||
## ITS-5G
|
||||
standard vehicle to vehicle/infrastructure communication systems from IEEE
|
||||
|
||||
## CSMA/CA
|
||||
algo permettant de baisser la probabilité de collision entre packet
|
||||
|
||||
## OFDM
|
||||
Orthogonal Frequency Division Multiplexing
|
||||
|
||||
## OCB
|
||||
Outside the Context of a Basic Service Set
|
||||
|
||||
# C-V2X
|
||||
## RB
|
||||
Ressource Block
|
||||
|
||||
## TB
|
||||
Transport Block
|
||||
|
||||
## PSSCH
|
||||
Physical Sidelink Shared Channels
|
||||
[PSCCH](https://www.ni.com/docs/en-US/bundle/rfmx-waveform-creator/page/v2x-sidelink-communication.html#:~:text=Physical%20sidelink%20control%20channel%20(PSCCH,consecutive%20resource%20blocks%20(RBs).)
|
||||
|
||||
## SCI
|
||||
Sidelink Control Information pour contrôler les informations envoyées
|
||||
|
||||
## SB-SPS
|
||||
Sensing-Based Semi-Persistent Scheduling
|
||||
|
||||
## RC
|
||||
Reselection Counter, c'est ce qui va permettre d'éviter des collisions entre les différents véhicules utilisant les mêmes canaux
|
||||
|
||||
## MSC
|
||||
Modulation and Coding Scheme, détermine comment est transmis les datas (QPSK, ...), parmi la table suivante avec un RE (Resource Element)
|
||||
|
||||
## SCS
|
||||
SubCarrier Spacing, l'espacement entre les fréquences sous-porteuses d'un protocole
|
||||
|
||||
## eNB
|
||||
[Evolved Node B](https://www.artizanetworks.com/resources/tutorials/what_lteenb.html)
|
||||
|
||||
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,18 @@
|
||||
# Wireless communication
|
||||
|
||||
## [Protocol](/Wireless-Communication/Protocol/)
|
||||
- [glossaire](/Wireless-Communication/Protocol/glossary)
|
||||
- [C-V2X vs ITS-G5](/Wireless-Communication/Protocol/C-V2X-vs-ITS-G5.md)
|
||||
- [IEEE 802.11](/Wireless-Communication/Protocol/IEEE%20802.11)
|
||||
- [V2X](/Wireless-Communication/Protocol/V2X)
|
||||
- [DECT NR+](/Wireless-Communication/Protocol/DECT-NR-+.md)
|
||||
- [Visible light communication](/Wireless-Communication/Protocol/Visible-light-communication.md)
|
||||
|
||||
|
||||
## [Train](/Wireless-Communication/Train/)
|
||||
- [Glossaire](/Wireless-Communication/Train/glossary.md)
|
||||
- [Channel model for performance evaluation](/Wireless-Communication/Train/Channel-model-for-performance-evaluation.md)
|
||||
- [FRMC - Projet 5GRail](/Wireless-Communication/Train/FRMC-Projet-5GRail.md)
|
||||
- [WTCMS](/Wireless-Communication/Train/WTCMS.md)
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# Channel model for performance evaluation
|
||||
|
||||
## Tapped delay line
|
||||
|
||||

|
||||
|
||||
Il faut imaginer qu'après un élément de retard on vient rajouter un le signal retarder et on devrait obtenir en sortie une
|
||||
sorte d'escalier si on regarde assez petit. Cela, est ma réfléction en regardant le graphe au-dessus.
|
||||
|
||||
Il permet de simuler une communication sans file en simulant les pilonnes qui relayent le signal avec un retard
|
||||
s'additionnant au signal de base. Ensuite, il ne faut pas oublier qu'un effet doppler rentre en ligne de compte à chaque élément
|
||||
s'ajoutant sur le chemin de la transmission entre pilonnes.
|
||||
|
||||
## Environnement des chemins de fer
|
||||
Il faut prendre en compte la vitesse, le lieu (tunnel pour les métro, pilonne, ...)
|
||||
|
||||
On trouve 3 scénarios de communications :
|
||||
- Train-to-Ground (T2G)
|
||||
- Train-to-Train (T2T)
|
||||
- intra-train communications
|
||||
|
||||
## Conclusion
|
||||
Le papier montre qu'il existe plein de modèle de canaux, mais il ne prenne pas en compte tous les scénarios. Seulement les suivants :
|
||||
- Montagneux
|
||||
- Rural
|
||||
- Viaduque
|
||||
- de coupure
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# FRMC Projet 5GRail
|
||||
Le document décrit comment le projet 5GRail a été prévu et simulé. Le but est de réaliser une simulation end-to-end pour
|
||||
plusieurs services utilisé par les trains (les fournisseurs). Le tout est séparé en plusieurs workpackage :
|
||||
|
||||
WP1 : dirigé par International Union of Railways (UIC)
|
||||
|
||||
WP2 : OBapp (on board applications) et TSapp (TrackSide application)
|
||||
|
||||
WP3 : fait au laboratoire nokia à Budapest
|
||||
- 5G/FRMCS-based [MCPTT](/Wireless-Communication/Train/glossary.md#mcptt) point-to-point and group voice communication realized by Siemens,
|
||||
- 5G/FRMCS-based Railway Emergency Call (REC) realized by Siemens,
|
||||
- [ETCS](/Wireless-Communication/Train/glossary.md#etcs) realized by CAF,
|
||||
- Train Control and Monitoring System (TCMS) realized by CAF,
|
||||
- Non-critical video application and CCTV archive transfer realized by Teleste.
|
||||
|
||||
WP4 : liaison des sites via VPN pour les testes
|
||||
|
||||
WP5 : testes réels
|
||||
|
||||
Fonctionnement :
|
||||
Le communication se fait au moyen de gateways qui permettent d'envoyer les données entre les trains et les stations de contrôle
|
||||
|
||||
Résultats des essais en laboratoires :
|
||||
- le premier appel end-to-end FRMCS
|
||||
- le management de la qualité des services en utilisant la méthode [DSCP](/Wireless-Communication/Train/glossary.md#DSCP)
|
||||
- les scénarios de changement de réseau 5G avec le [RV](/Wireless-Communication/Train/glossary.md#RV) activé
|
||||
- Préparations des activités pour les tests du package 5 c-à-d, **les effet de la vitesse sur les fréquence radios**
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<header><h1>
|
||||
Protocol de communication wireless
|
||||
</h1></header>
|
||||
|
||||
- [Glossaire](/Wireless-Communication/Train/glossary)
|
||||
- [Channel model for performance evaluation](/Wireless-Communication/Train/Channel-model-for-performance-evaluation)
|
||||
- [FRMC - Projet 5GRail](/Wireless-Communication/Train/FRMC-Projet-5GRail)
|
||||
- [WTCMS](/Wireless-Communication/Train/WTCMS)
|
||||
@@ -0,0 +1,50 @@
|
||||
# Introduction
|
||||
Les trains utilise la technologie [GSM-R](/Wireless-Communication/Train/glossary.md#gsm-r) qui est
|
||||
l'ancien réseau mobile des trains. Dans notre cas, on veux utiliser la technologie
|
||||
[WTCMS](/Wireless-Communication/Train/glossary.md#wtcms).
|
||||
|
||||
# WTCMS
|
||||
Cette nouvelle technologie doit permettre d'enlever [ETB](/Wireless-Communication/Train/glossary.md#etb).
|
||||
|
||||
## Prérequis
|
||||
|
||||
1. la latence ne dois pas excéder 3 fois le temps de cycle du train
|
||||
2. le trains peut avoir 32 wagon avec une distance totale de 850m.
|
||||
3. Il doit être capable de d'interconnecter jsuqu'à 32 nodes
|
||||
4. Le changement dois être invisible pour les end-users
|
||||
|
||||
## Topologies alternatives
|
||||
### Variant 1 : Topologie linéaire
|
||||
Chacun des noeuds pour parler directement à ses voisins. Il peut communiquer avec le reste du réseau aussi en passant
|
||||
par ses voisins.
|
||||
|
||||
Cette manière permet d'atlerner les fréquences de communications entre chaque wagon. De cette manière, on utilise un moins
|
||||
large spectre dans les fréquences.
|
||||
|
||||
Son défaut est la latence total.
|
||||
Par contre, ce serait bien plus facile de mettre en place. Puisque le système est similaire à ce qui est déjà en place.
|
||||
Mais, la solution supprime pas tellement le réseau filaire actuel.
|
||||
|
||||
### Variant 2a : Topologie en Mesh
|
||||
Dans cette variante la topologie mesh permet de communiquer directement avec n'importe quel autre noeud du réseau. Mais,
|
||||
la latence dépend de la vitesse à laquelle chaque noeud peut réceptionner et transmettre.
|
||||
|
||||
### Variant 2b : Topologie en Mesh
|
||||
Dans cette variante, Il utilise la [Variant 1](#variant-1-topologie-linéaire) améliorée. C'est à dire, les noeuds peuvent
|
||||
communique transmette à leur voisin, s'ils ne peuvent pas atteindre le noeud cible.
|
||||
|
||||
### Conclusion
|
||||
La variant 1 est la plus simple à mettre en place. Mais, il garde la majorité des fils et garde une latence proportionnelle
|
||||
au chemin à parcourir du packet.
|
||||
|
||||
La variant 2a supprime le problème de délai de la variante 1, mais ce n'est pas réaliste de mettre une antenne pour émettre
|
||||
à 850m sur chaque wagon.
|
||||
|
||||
La variante 2b réduit la latence de la variante 1, en gardant des antennes qui emettent à 250m.
|
||||
|
||||
Finalement, la variante 2b est la plus réaliste à mettre en place.
|
||||
|
||||
## Comparaison de différent protocole
|
||||

|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# Global glossary
|
||||
|
||||
## CDL
|
||||
Cluster-Delay-Line
|
||||
|
||||
## CIR
|
||||
Channel Impulse Response
|
||||
|
||||
## LoS
|
||||
Line of Sight, le chemin direct entre un émetteur et un récepteur
|
||||
|
||||
## NLOS
|
||||
Non-Line-Of-Sight
|
||||
|
||||
## CRS
|
||||
cell-specific reference signal
|
||||
|
||||
## IP
|
||||
Intersection Point, le point d'intercection entre les rails et le le pilonne de transmission
|
||||
|
||||
## UL
|
||||
Uplink, c'est la cummunication entre le qqch au sol et le satellite
|
||||
|
||||
## DL
|
||||
Downlink, c'est la communication entre le satellite et le qqch au sol
|
||||
|
||||
## OAI
|
||||
Open Air Interface, c'est un projet open source pour les réseaux cellulaires RAN (Radio Access Network) et CN (Core Network)
|
||||
|
||||
## FRMCS
|
||||
Future Railway Mobile Communication System
|
||||
|
||||
## DSCP
|
||||
permet de classer le packet dans un réseau IP par ordre de priorité
|
||||
|
||||
## ETCS
|
||||
European Train Control System
|
||||
|
||||
## ATO
|
||||
Automatic Train Operation
|
||||
|
||||
## RV
|
||||
Remote Vision
|
||||
|
||||
## 5QI
|
||||
standard pour le 5G QoS identifiant, pour savoir la qualité du service
|
||||
|
||||
## MCPTT
|
||||
Mission Critical Push-to-Talk (utilisé pour la communication critique avec la pression sur une touche pour parler simplement et rapidement)
|
||||
|
||||
## PIS
|
||||
Passenger Information System
|
||||
|
||||
## RMR
|
||||
Railway Mobile Radio, réseau de communication pour les trains
|
||||
|
||||
## TDL
|
||||
Tapped-Delay-Line
|
||||
|
||||
## Jitter
|
||||
la variance de la latence entre le moment de la transmission et le moment de la réception
|
||||
|
||||
## RAT
|
||||
Radio Access Technology, la technologie utilisée pour accéder au réseau
|
||||
|
||||
## TCMS
|
||||
Train Control and Management System
|
||||
|
||||
## WTCMS
|
||||
Wireless Train Control and Management System
|
||||
|
||||
## GSM-R
|
||||
Global System for Mobile Communications - Railway, c'est un standard ancien de communication pour les trains
|
||||
|
||||
## WLTB
|
||||
Wireless Local Train Backbone, c'est un réseau de communication sans fil pour les trains
|
||||
|
||||
## ETB
|
||||
Ethernet Train Backbone, c'est un réseau de communication basé sur Ethernet pour les trains qui va être remplacer par le WLTB
|
||||
|
||||
## ETBN
|
||||
Ethernet Train Backbone Network, c'est un réseau de communication basé sur Ethernet pour les trains qui va être remplacer par le WLTB
|
||||
|
||||
## OMTS
|
||||
On-board Multimedia and Telematics
|
||||
|
||||
## COTS
|
||||
Commercial Off-The-Shelf, comprend l'ensemble des produits pouvant être utilisé achetable dans le commerce
|
||||
|
||||
## NG-TCN
|
||||
Next Generation Train Control Network,
|
||||
|
||||
## TSM
|
||||
Time Sensitve Networking, fonctionnalités qui servent à garantir les services dans le temps, déterministe
|
||||
|
||||
## Consist
|
||||
un ensemble de wagons qui sont attachés ensemble pour former par exemple une série de wagon 2ième classe ou autre chose
|
||||
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
@@ -0,0 +1,13 @@
|
||||
* [Protocol](/Wireless-Communication/Protocol/)
|
||||
* [glossaire](/Wireless-Communication/Protocol/glossary)
|
||||
* [C-V2X vs ITS-G5](/Wireless-Communication/Protocol/C-V2X-vs-ITS-G5)
|
||||
* [IEEE 802.11](/Wireless-Communication/Protocol/IEEE%20802.11)
|
||||
* [V2X](/Wireless-Communication/Protocol/V2X)
|
||||
* [DECT NR+](/Wireless-Communication/Protocol/DECT-NR-+.md)
|
||||
* [Visible light communication](/Wireless-Communication/Protocol/Visible-light-communication.md)
|
||||
|
||||
* [Train](/Wireless-Communication/Train/)
|
||||
* [glossaire](/Wireless-Communication/Train/glossary)
|
||||
* [Channel model for performance evaluation](/Wireless-Communication/Train/Channel-model-for-performance-evaluation)
|
||||
* [FRMC - Projet 5GRail](/Wireless-Communication/Train/FRMC-Projet-5GRail)
|
||||
* [WTCMS](/Wireless-Communication/Train/WTCMS)
|
||||
@@ -3,11 +3,13 @@
|
||||
* [SDi - Systèmes Distribués](/infotro/SDi/)
|
||||
* [Eln - Électronique](/infotro/Eln/)
|
||||
* [SEm - Systèmes Embarqués](/infotro/SEm/)
|
||||
* [MSE](/MSE/)
|
||||
* [SRE](/MSE/SRE/)
|
||||
* [Embed](/Embed/)
|
||||
* [STM32](/Embed/STM32/)
|
||||
* [nRF](/Embed/nRF/)
|
||||
* [HES-SO](/HES-SO/)
|
||||
* [OneDrive - Linux](/HES-SO/OneDrive.md)
|
||||
* [Teams - Linux](/HES-SO/Teams.md)
|
||||
* [Wireless Communication](/Wireless-Communication/)
|
||||
* [Protocol](/Wireless-Communication/Protocol/)
|
||||
* [Train](/Wireless-Communication/Train/)
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"/homelab/backup.html": [
|
||||
"family"
|
||||
],
|
||||
"/security/ssh.html": [
|
||||
"admin"
|
||||
],
|
||||
"/thermo/fluids.html": [
|
||||
"family",
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
// Command parser scans a directory of Typst sources (a shiroa book) for
|
||||
// access-group tags and generates the acl.json consumed by the server.
|
||||
//
|
||||
// A page declares its access groups with a Typst let binding at the top of
|
||||
// the file:
|
||||
//
|
||||
// #let access-groups = ("family", "admin")
|
||||
//
|
||||
// Pages without a tag are public. The generated acl.json maps the final URL
|
||||
// of each page (e.g. "/thermo/fluids.html") to its authorized groups.
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Matches: #let access-groups = ("group1", "group2")
|
||||
var accessGroupsRe = regexp.MustCompile(`(?m)^\s*#let\s+access-groups\s*=\s*\(([^)]*)\)`)
|
||||
|
||||
// Files that are never pages: book metadata, page layout, and partials
|
||||
// (files starting with "_") imported by other pages.
|
||||
func isSkipped(base string) bool {
|
||||
return base == "book.typ" || base == "template.typ" || strings.HasPrefix(base, "_")
|
||||
}
|
||||
|
||||
func main() {
|
||||
inDir := flag.String("in", "docs", "input directory containing the shiroa book (.typ sources)")
|
||||
outFile := flag.String("out", "acl.json", "output acl.json file")
|
||||
flag.Parse()
|
||||
|
||||
acl := make(map[string][]string)
|
||||
|
||||
err := filepath.WalkDir(*inDir, func(p string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
base := d.Name()
|
||||
if d.IsDir() {
|
||||
// Skip build output and hidden directories.
|
||||
if p != *inDir && (base == "dist" || strings.HasPrefix(base, ".")) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if filepath.Ext(base) != ".typ" || isSkipped(base) {
|
||||
return nil
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading %s: %w", p, err)
|
||||
}
|
||||
groups := extractGroups(content)
|
||||
if len(groups) == 0 {
|
||||
return nil // public page, no rule needed
|
||||
}
|
||||
|
||||
relPath, err := filepath.Rel(*inDir, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// docs/<path>.typ compiles to dist/<path>.html, served at /<path>.html
|
||||
urlPath := "/" + strings.TrimSuffix(filepath.ToSlash(relPath), ".typ") + ".html"
|
||||
|
||||
acl[urlPath] = groups
|
||||
log.Printf("protected %s -> %s", urlPath, strings.Join(groups, ", "))
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("scanning %s: %v", *inDir, err)
|
||||
}
|
||||
|
||||
out, err := json.MarshalIndent(acl, "", " ")
|
||||
if err != nil {
|
||||
log.Fatalf("encoding acl: %v", err)
|
||||
}
|
||||
out = append(out, '\n')
|
||||
if err := os.WriteFile(*outFile, out, 0o644); err != nil {
|
||||
log.Fatalf("writing %s: %v", *outFile, err)
|
||||
}
|
||||
|
||||
fmt.Printf("generated %s (%d protected pages)\n", *outFile, len(acl))
|
||||
}
|
||||
|
||||
func extractGroups(content []byte) []string {
|
||||
m := accessGroupsRe.FindSubmatch(content)
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
var groups []string
|
||||
for _, part := range strings.Split(string(m[1]), ",") {
|
||||
part = strings.TrimSpace(part)
|
||||
part = strings.Trim(part, "\"'")
|
||||
if part != "" {
|
||||
groups = append(groups, part)
|
||||
}
|
||||
}
|
||||
return groups
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
// Command server is the Yggdrasil web server: a single static binary that
|
||||
// embeds the compiled documentation (dist/) and its access control list
|
||||
// (acl.json), serves public pages directly, and gates protected pages
|
||||
// behind OIDC authentication (Authentik).
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/Klagarge/yggdrasil"
|
||||
"github.com/Klagarge/yggdrasil/internal/acl"
|
||||
"github.com/Klagarge/yggdrasil/internal/middleware"
|
||||
"github.com/Klagarge/yggdrasil/internal/oidc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
listenAddr := envOr("LISTEN_ADDR", ":8080")
|
||||
sessionTTL := envDuration("SESSION_TTL", 12*time.Hour)
|
||||
cookieSecure := envOr("COOKIE_SECURE", "true") == "true"
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
list, err := acl.Load(yggdrasil.ACLData())
|
||||
if err != nil {
|
||||
log.Fatalf("refusing to start: %v", err) // fail closed
|
||||
}
|
||||
|
||||
sessions := middleware.NewSessionStore(sessionTTL)
|
||||
go sessions.StartJanitor(ctx, 10*time.Minute)
|
||||
|
||||
auth := setupOIDC(ctx)
|
||||
|
||||
dist, err := yggdrasil.DistFS()
|
||||
if err != nil {
|
||||
log.Fatalf("embedded dist: %v", err)
|
||||
}
|
||||
|
||||
gate := middleware.NewAccessControl(list, sessions, auth, cookieSecure)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("GET /login", gate.Login)
|
||||
mux.HandleFunc("GET /callback", gate.Callback)
|
||||
mux.HandleFunc("GET /logout", gate.Logout)
|
||||
mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("ok\n"))
|
||||
})
|
||||
// The search index contains the text of protected pages: never serve it.
|
||||
mux.HandleFunc("/searchindex.json", http.NotFound)
|
||||
mux.Handle("/", gate.Wrap(http.FileServerFS(dist)))
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: listenAddr,
|
||||
Handler: gate.SecurityHeaders(mux),
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
ReadTimeout: 30 * time.Second,
|
||||
WriteTimeout: 60 * time.Second,
|
||||
IdleTimeout: 2 * time.Minute,
|
||||
}
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() { errCh <- srv.ListenAndServe() }()
|
||||
log.Printf("listening on %s (%d protected pages)", listenAddr, len(list))
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Fatalf("server error: %v", err)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
}
|
||||
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
if err := srv.Shutdown(shutdownCtx); err != nil {
|
||||
log.Printf("shutdown: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// setupOIDC builds the OIDC client from the environment. Without OIDC
|
||||
// configuration the server still serves public pages; protected pages
|
||||
// answer 503 instead of being exposed.
|
||||
func setupOIDC(ctx context.Context) *oidc.Authenticator {
|
||||
cfg, err := oidc.LoadConfigFromEnv()
|
||||
if err != nil {
|
||||
log.Printf("OIDC disabled (%v) - protected pages will answer 503", err)
|
||||
return nil
|
||||
}
|
||||
auth, err := oidc.NewAuthenticator(ctx, cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("OIDC setup: %v", err)
|
||||
}
|
||||
log.Printf("OIDC enabled (issuer: %s)", cfg.IssuerURL)
|
||||
return auth
|
||||
}
|
||||
|
||||
func envOr(key, def string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func envDuration(key string, def time.Duration) time.Duration {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
if d, err := time.ParseDuration(v); err == nil {
|
||||
return d
|
||||
}
|
||||
log.Printf("invalid %s %q, using %s", key, v, def)
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
@@ -1,22 +0,0 @@
|
||||
#import "@preview/shiroa:0.4.0": *
|
||||
#show: book
|
||||
|
||||
#book-meta(
|
||||
title: "Yggdrasil",
|
||||
authors: ("Klagarge",),
|
||||
summary: [
|
||||
#prefix-chapter("index.typ")[Home]
|
||||
|
||||
= Thermodynamics
|
||||
- #chapter("thermo/heat-pumps.typ", section: "1")[Heat pumps]
|
||||
- #chapter("thermo/fluids.typ", section: "2")[Fluid mechanics]
|
||||
|
||||
= Security
|
||||
- #chapter("security/opsec.typ", section: "3")[Opsec basics]
|
||||
- #chapter("security/ssh.typ", section: "4")[SSH hardening]
|
||||
|
||||
= Homelab
|
||||
- #chapter("homelab/self-hosting.typ", section: "5")[Self-hosting]
|
||||
- #chapter("homelab/backup.typ", section: "6")[Backups]
|
||||
],
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
// Access control: only members of these Authentik groups can read this page.
|
||||
// Delete this line to make the page public.
|
||||
#let access-groups = ("family",)
|
||||
|
||||
#import "../template.typ": project
|
||||
#show: project
|
||||
|
||||
= Backups
|
||||
|
||||
Protected page placeholder about backups: the 3-2-1 rule, restic,
|
||||
off-site replication...
|
||||
@@ -1,7 +0,0 @@
|
||||
#import "../template.typ": project
|
||||
#show: project
|
||||
|
||||
= Self-hosting
|
||||
|
||||
Public page placeholder about self-hosting: Docker/Podman, reverse
|
||||
proxies, DNS, VLANs...
|
||||
@@ -1,17 +0,0 @@
|
||||
#import "template.typ": project
|
||||
#show: project
|
||||
|
||||
= Yggdrasil
|
||||
|
||||
Welcome to the Yggdrasil knowledge base, built with Typst, Shiroa and a Go
|
||||
server.
|
||||
|
||||
*How it works:*
|
||||
|
||||
- Pages are written in Typst under `docs/` and compiled to static HTML.
|
||||
- A Go server embeds the HTML and an access-control list (`acl.json`).
|
||||
- Pages tagged with `#let access-groups = ("family", "admin")` are only
|
||||
visible to authenticated users in those groups; everything else is
|
||||
public.
|
||||
|
||||
Browse the categories in the sidebar to get started.
|
||||
@@ -1,7 +0,0 @@
|
||||
#import "../template.typ": project
|
||||
#show: project
|
||||
|
||||
= Opsec basics
|
||||
|
||||
Public page placeholder about operational security: threat models,
|
||||
password managers, 2FA...
|
||||
@@ -1,11 +0,0 @@
|
||||
// Access control: only members of these Authentik groups can read this page.
|
||||
// Delete this line to make the page public.
|
||||
#let access-groups = ("admin",)
|
||||
|
||||
#import "../template.typ": project
|
||||
#show: project
|
||||
|
||||
= SSH hardening
|
||||
|
||||
Protected page placeholder about SSH: key management, ciphers,
|
||||
session hardening, bastion hosts...
|
||||
@@ -1,44 +0,0 @@
|
||||
#import "@preview/shiroa:0.4.0": get-page-width, is-html-target, is-pdf-target, is-web-target, templates, x-target
|
||||
#import templates: *
|
||||
|
||||
// Metadata
|
||||
#let page-width = get-page-width()
|
||||
#let is-html-target = is-html-target() // target.starts-with("html")
|
||||
#let is-pdf-target = is-pdf-target() // target.starts-with("pdf")
|
||||
#let is-web-target = is-web-target() // target.starts-with("web")
|
||||
|
||||
#let project(body) = {
|
||||
// set web/pdf page properties
|
||||
set page(
|
||||
width: page-width,
|
||||
// for a website, we don't need pagination.
|
||||
height: auto,
|
||||
) if is-pdf-target or is-web-target
|
||||
|
||||
// remove margins for web target
|
||||
set page(margin: (
|
||||
// reserved beautiful top margin
|
||||
top: 20pt,
|
||||
// Typst is setting the page's bottom to the baseline of the last line of text. So bad :(.
|
||||
bottom: 0.5em,
|
||||
// remove rest margins.
|
||||
rest: 0pt,
|
||||
)) if is-web-target
|
||||
|
||||
let web-theme = if x-target.starts-with("html") and not x-target.starts-with("html-wrapper") {
|
||||
"starlight"
|
||||
} else {
|
||||
"mdbook"
|
||||
}
|
||||
show: if web-theme == "starlight" {
|
||||
import "@preview/shiroa-starlight:0.4.0": starlight
|
||||
starlight.with(include "book.typ")
|
||||
} else if web-theme == "mdbook" {
|
||||
import "@preview/shiroa-mdbook:0.4.0": mdbook
|
||||
mdbook.with(include "book.typ")
|
||||
} else {
|
||||
panic("Unknown web theme: " + web-theme)
|
||||
}
|
||||
|
||||
body
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Access control: only members of these Authentik groups can read this page.
|
||||
// Delete this line to make the page public.
|
||||
#let access-groups = ("family", "admin")
|
||||
|
||||
#import "../template.typ": project
|
||||
#show: project
|
||||
|
||||
= Fluid mechanics
|
||||
|
||||
Protected page placeholder about fluid mechanics: statics, dynamics,
|
||||
Bernoulli's principle, pressure losses...
|
||||
@@ -1,7 +0,0 @@
|
||||
#import "../template.typ": project
|
||||
#show: project
|
||||
|
||||
= Heat pumps
|
||||
|
||||
Public page placeholder about heat pumps: COP, seasonal performance,
|
||||
refrigerant cycles...
|
||||
@@ -1,26 +0,0 @@
|
||||
// Package yggdrasil embeds the build artifacts: the static site compiled by
|
||||
// shiroa (dist/) and the access control list (acl.json). Embedding them at
|
||||
// the repository root lets //go:embed see both directories relative to this
|
||||
// file; `go build` requires acl.json and dist/ to exist (see the Justfile).
|
||||
package yggdrasil
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
//go:embed all:dist
|
||||
var distFS embed.FS
|
||||
|
||||
//go:embed acl.json
|
||||
var aclData []byte
|
||||
|
||||
// DistFS returns the embedded static site.
|
||||
func DistFS() (fs.FS, error) {
|
||||
return fs.Sub(distFS, "dist")
|
||||
}
|
||||
|
||||
// ACLData returns the embedded acl.json.
|
||||
func ACLData() []byte {
|
||||
return aclData
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
module github.com/Klagarge/yggdrasil
|
||||
|
||||
go 1.27.1
|
||||
|
||||
require (
|
||||
github.com/coreos/go-oidc/v3 v3.21.0
|
||||
golang.org/x/oauth2 v0.37.0
|
||||
)
|
||||
|
||||
require github.com/go-jose/go-jose/v4 v4.1.4 // indirect
|
||||
@@ -1,6 +0,0 @@
|
||||
github.com/coreos/go-oidc/v3 v3.21.0 h1:wZo4Q9Pum8dYEj0eMUPrqR+kvuGkeUplbLpNCkBqoWM=
|
||||
github.com/coreos/go-oidc/v3 v3.21.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
golang.org/x/oauth2 v0.37.0 h1:JUlcxA8oAtauLfiH8FX2/FkAWHAdi0QtGCGc+hofE98=
|
||||
golang.org/x/oauth2 v0.37.0/go.mod h1:IxwZNxUULJmpBFf9K/9NTMSIfZZuvuTy1gGxhigP/58=
|
||||
@@ -189,9 +189,6 @@
|
||||
<link rel="stylesheet" href="//unpkg.com/docsify-accordion/src/style.css">
|
||||
<script src="//unpkg.com/docsify-accordion/src/index.js"></script>
|
||||
|
||||
<!----- Terminal ----->
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-terminal-block@1.0.3"></script>
|
||||
|
||||
<!----- Last time updated ----->
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-updated/src/time-updater.js"></script>
|
||||
|
||||
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |