Compare commits

..

2 Commits

Author SHA1 Message Date
Jesús Pérez
b66ebbfeaf
chore: move rust-test and cargo-deny to manual stage
Some checks failed
Rust CI / Security Audit (push) Has been cancelled
Rust CI / Check + Test + Lint (nightly) (push) Has been cancelled
Rust CI / Check + Test + Lint (stable) (push) Has been cancelled
Nickel Type Check / Nickel Type Checking (push) Has been cancelled
2026-03-13 21:03:06 +00:00
Jesús Pérez
d59644b96f
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
  --gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
183 changed files with 48685 additions and 1111 deletions

View File

@ -75,6 +75,9 @@ let d = import "../ontology/defaults/core.ncl" in
"adrs/_template.ncl", "adrs/_template.ncl",
"adrs/adr-001-protocol-as-standalone-project.ncl", "adrs/adr-001-protocol-as-standalone-project.ncl",
"adrs/adr-002-daemon-for-caching-and-notification-barrier.ncl", "adrs/adr-002-daemon-for-caching-and-notification-barrier.ncl",
"adrs/adr-003-qa-and-knowledge-persistence-as-ncl.ncl",
"adrs/adr-004-ncl-pipe-bootstrap-pattern.ncl",
"adrs/adr-005-unified-auth-session-model.ncl",
"CHANGELOG.md", "CHANGELOG.md",
], ],
}, },
@ -157,18 +160,77 @@ let d = import "../ontology/defaults/core.ncl" in
name = "Ontoref Daemon", name = "Ontoref Daemon",
pole = 'Yang, pole = 'Yang,
level = 'Practice, level = 'Practice,
description = "Runtime support daemon for the ontoref protocol. Provides NCL export caching, file watching, actor registry, notification barrier, HTTP API, MCP server (stdio + streamable-HTTP), Q&A NCL persistence, quick-actions catalog, and passive drift observation. Reads .ontoref/config.ncl at startup.", description = "Runtime support daemon for the ontoref protocol. Provides NCL export caching, file watching, actor registry, notification barrier, HTTP API, MCP server (stdio + streamable-HTTP), Q&A NCL persistence, quick-actions catalog, passive drift observation, and unified auth/session management (key exchange, Bearer tokens, per-project and daemon-level admin, session list/revoke). Launched via ADR-004 NCL pipe bootstrap: nickel export config.ncl | ontoref-daemon.bin --config-stdin. Binary installed as ontoref-daemon.bin; bootstrapper as ontoref-daemon.",
invariant = false, invariant = false,
artifact_paths = [ artifact_paths = [
"crates/ontoref-daemon/", "crates/ontoref-daemon/",
"install/ontoref-daemon-boot",
"install/install.nu",
"nats/streams.json",
"reflection/modules/services.nu", "reflection/modules/services.nu",
"crates/ontoref-daemon/src/ui/qa_ncl.rs", "crates/ontoref-daemon/src/ui/qa_ncl.rs",
"crates/ontoref-daemon/src/ui/drift_watcher.rs", "crates/ontoref-daemon/src/ui/drift_watcher.rs",
"crates/ontoref-daemon/src/mcp/mod.rs", "crates/ontoref-daemon/src/mcp/mod.rs",
"crates/ontoref-daemon/src/session.rs",
"crates/ontoref-daemon/src/ui/auth.rs",
"crates/ontoref-daemon/src/ui/login.rs",
"justfiles/ci.just", "justfiles/ci.just",
], ],
}, },
d.make_node {
id = "unified-auth-model",
name = "Unified Auth Model",
pole = 'Yang,
level = 'Practice,
description = "All surfaces (CLI, UI, MCP) exchange a raw key for a UUID v4 session token via POST /sessions (30-day lifetime, O(1) lookup vs O(~100ms) argon2 per request). Project keys carry role (admin|viewer) and label for audit trail. Daemon admin sessions use virtual slug '_daemon'. ONTOREF_TOKEN injected as Bearer by CLI automatically. Sessions have a stable public id (distinct from bearer token) for safe list/revoke operations. Key rotation revokes all sessions for the rotated project.",
invariant = false,
artifact_paths = [
"crates/ontoref-daemon/src/session.rs",
"crates/ontoref-daemon/src/api.rs",
"crates/ontoref-daemon/src/registry.rs",
"crates/ontoref-daemon/src/ui/login.rs",
"crates/ontoref-daemon/src/ui/auth.rs",
"reflection/modules/store.nu",
"install/resources/schemas/ontoref-project.ncl",
],
},
d.make_node {
id = "project-onboarding",
name = "Project Onboarding",
pole = 'Yang,
level = 'Practice,
description = "Idempotent onboarding via `ontoref setup`. Creates .ontoref/project.ncl, .ontoref/config.ncl (with logo auto-detection in assets/), .ontology/ scaffold, adrs/, reflection/modes/, backlog.ncl, qa.ncl, git hooks, and registers in projects.ncl. Supports --kind (repo_kind) and --parent (framework layers + browse modes for implementation children). Bootstrap key generation via --gen-keys ['admin:label' 'viewer:label']: idempotent (no-op if keys exist), hashes via daemon binary, prints passwords once.",
invariant = false,
artifact_paths = [
"reflection/bin/ontoref.nu",
"templates/project.ncl",
"templates/ontoref-config.ncl",
"templates/ontology/",
"install/gen-projects.nu",
"install/resources/schemas/ontoref-project.ncl",
],
},
d.make_node {
id = "daemon-config-management",
name = "Daemon Config Management",
pole = 'Yang,
level = 'Practice,
description = "Install and configuration infrastructure for ontoref-daemon. Global config at ~/.config/ontoref/config.ncl (Nickel, type-checked). Browser-based editing via typedialog roundtrip: form (config.ncl) → browser → Tera template (config.ncl.j2) → updated config.ncl. CI guard (check-config-sync.nu) enforces form/template parity on every commit. Global NATS topology at ~/.config/ontoref/streams.json; project-local override via nats/streams.json and NATS_STREAMS_CONFIG env var. Config validation + liveness probes via config-setup.nu.",
invariant = false,
artifact_paths = [
"install/resources/config.ncl",
"install/resources/streams.json",
"install/config-setup.nu",
"install/check-config-sync.nu",
"reflection/forms/config.ncl",
"reflection/forms/config.ncl.j2",
"reflection/nulib/bootstrap.nu",
],
},
d.make_node { d.make_node {
id = "qa-knowledge-store", id = "qa-knowledge-store",
name = "Q&A Knowledge Store", name = "Q&A Knowledge Store",
@ -239,6 +301,9 @@ let d = import "../ontology/defaults/core.ncl" in
{ from = "qa-knowledge-store", to = "coder-process-memory", kind = 'Complements, weight = 'High, { from = "qa-knowledge-store", to = "coder-process-memory", kind = 'Complements, weight = 'High,
note = "Q&A is the persistent layer; coder.nu is the session capture layer. Together they form the full memory stack." }, note = "Q&A is the persistent layer; coder.nu is the session capture layer. Together they form the full memory stack." },
{ from = "ontoref-daemon", to = "qa-knowledge-store", kind = 'Contains, weight = 'High }, { from = "ontoref-daemon", to = "qa-knowledge-store", kind = 'Contains, weight = 'High },
{ from = "daemon-config-management", to = "ontoref-daemon", kind = 'DependsOn, weight = 'High },
{ from = "daemon-config-management", to = "adopt-ontoref-tooling", kind = 'Complements, weight = 'Medium,
note = "Config management is part of the adoption surface — new projects get config.ncl and streams.json during install." },
# Quick Actions edges # Quick Actions edges
{ from = "quick-actions", to = "reflection-modes", kind = 'DependsOn, weight = 'High, { from = "quick-actions", to = "reflection-modes", kind = 'DependsOn, weight = 'High,
@ -254,5 +319,18 @@ let d = import "../ontology/defaults/core.ncl" in
{ from = "drift-observation", to = "reflection-modes", kind = 'DependsOn, weight = 'High, { from = "drift-observation", to = "reflection-modes", kind = 'DependsOn, weight = 'High,
note = "Invokes sync-ontology mode steps (scan, diff) as read-only sub-processes." }, note = "Invokes sync-ontology mode steps (scan, diff) as read-only sub-processes." },
# Unified Auth Model edges
{ from = "unified-auth-model", to = "ontoref-daemon", kind = 'ManifestsIn, weight = 'High },
{ from = "unified-auth-model", to = "no-enforcement", kind = 'Contradicts, weight = 'Low,
note = "Auth is opt-in per project (no keys = open deployment). When keys are configured enforcement is real, but the protocol itself never mandates it." },
{ from = "ontoref-daemon", to = "unified-auth-model", kind = 'Contains, weight = 'High },
# Project Onboarding edges
{ from = "project-onboarding", to = "adopt-ontoref-tooling", kind = 'Complements, weight = 'High,
note = "adopt-ontoref-tooling is the migration surface for existing projects; project-onboarding is the first-class setup for new projects." },
{ from = "project-onboarding", to = "unified-auth-model", kind = 'DependsOn, weight = 'Medium,
note = "--gen-keys bootstraps the first keys into project.ncl during setup." },
{ from = "project-onboarding", to = "daemon-config-management", kind = 'DependsOn, weight = 'Medium },
], ],
} }

View File

@ -28,9 +28,9 @@ m.make_manifest {
}, },
m.make_layer { m.make_layer {
id = "tooling", id = "tooling",
paths = ["reflection/", "ontoref"], paths = ["reflection/", "install/", "nats/", "templates/", "ontoref"],
committed = true, committed = true,
description = "Operational tooling: Nushell modules, modes, forms, dispatcher, and bash entry point.", description = "Operational tooling: Nushell modules, modes, forms, dispatcher, bash entry point, install scripts, default config resources, and NATS stream topology.",
}, },
m.make_layer { m.make_layer {
id = "crates", id = "crates",

View File

@ -25,7 +25,7 @@ let d = import "../ontology/defaults/state.ncl" in
to = "protocol-stable", to = "protocol-stable",
condition = "ADR-001 accepted, ontoref.dev published, at least two external projects consuming the protocol.", condition = "ADR-001 accepted, ontoref.dev published, at least two external projects consuming the protocol.",
catalyst = "First external adoption.", catalyst = "First external adoption.",
blocker = "ontoref.dev not yet published; no external consumers yet. Entry point UX fixed (no-args usage message, bash set -u safety).", blocker = "ontoref.dev not yet published; no external consumers yet. Auth model complete (session exchange, CLI Bearer, key rotation invalidation). Install pipeline: config form roundtrip and NATS topology operational; check-config-sync CI guard present.",
horizon = 'Months, horizon = 'Months,
}, },
], ],
@ -35,7 +35,7 @@ let d = import "../ontology/defaults/state.ncl" in
id = "self-description-coverage", id = "self-description-coverage",
name = "Self-Description Coverage", name = "Self-Description Coverage",
description = "How completely ontoref describes itself using its own protocol.", description = "How completely ontoref describes itself using its own protocol.",
current_state = "modes-and-web-present", current_state = "fully-self-described",
desired_state = "fully-self-described", desired_state = "fully-self-described",
horizon = 'Weeks, horizon = 'Weeks,
states = [], states = [],
@ -52,8 +52,8 @@ let d = import "../ontology/defaults/state.ncl" in
from = "modes-and-web-present", from = "modes-and-web-present",
to = "fully-self-described", to = "fully-self-described",
condition = "At least 3 ADRs accepted, reflection/backlog.ncl present, describe project returns complete picture.", condition = "At least 3 ADRs accepted, reflection/backlog.ncl present, describe project returns complete picture.",
catalyst = "ADR-001 and ADR-002 authored using ontoref against itself in session 2026-03-12.", catalyst = "ADR-001ADR-004 authored (4 ADRs present, 3+ threshold met). Auth model, project onboarding, and session management nodes added to core.ncl in session 2026-03-13.",
blocker = "1 more ADR needed to reach 3. reflection/backlog.ncl not present.", blocker = "none",
horizon = 'Weeks, horizon = 'Weeks,
}, },
], ],
@ -88,5 +88,46 @@ let d = import "../ontology/defaults/state.ncl" in
], ],
}, },
d.make_dimension {
id = "operational-mode",
name = "Operational Mode",
description = "Runtime connectivity mode: local (files only) or daemon (push-based DB projection). Auto-detected on each command; transitions trigger hook updates and sync. Daemon launched via ADR-004 NCL pipe bootstrap (ontoref-daemon-boot); NATS topology resolved from NATS_STREAMS_CONFIG env var (global ~/.config/ontoref/streams.json) or project-local nats/streams.json.",
current_state = "local",
desired_state = "daemon",
horizon = 'Continuous,
states = [
d.make_state {
id = "local",
name = "Local",
description = "No daemon. All operations read from files. Hooks are no-ops. Safe for offline or repo-only work.",
tension = 'Low,
},
d.make_state {
id = "daemon",
name = "Daemon",
description = "Daemon reachable. Ontology projected into DB on each sync. Hooks push on git merge/checkout. NATS events available.",
tension = 'Low,
},
],
transitions = [
d.make_transition {
from = "local",
to = "daemon",
condition = "Daemon reachable at ONTOREF_DAEMON_URL and DB available (if db feature enabled).",
catalyst = "Daemon started, network restored, or first onboarding after install.",
blocker = "Daemon not running or DB not configured.",
horizon = 'Continuous,
},
d.make_transition {
from = "daemon",
to = "local",
condition = "Daemon unreachable or DB unavailable.",
catalyst = "Network loss, daemon stopped, or offline work.",
blocker = "none",
horizon = 'Continuous,
},
],
},
], ],
} }

View File

@ -89,7 +89,7 @@ vim .typedialog/ci/config.ncl
**This project uses Nickel format by default** for all configuration files. **This project uses Nickel format by default** for all configuration files.
### Why Nickel? ### Why Nickel
- ✅ **Typed configuration** - Static type checking with `nickel typecheck` - ✅ **Typed configuration** - Static type checking with `nickel typecheck`
- ✅ **Documentation** - Generate docs with `nickel doc config.ncl` - ✅ **Documentation** - Generate docs with `nickel doc config.ncl`
@ -313,7 +313,7 @@ Edit `config.ncl` and add under `ci.tools`:
enable_pre_commit = false enable_pre_commit = false
``` ```
## Need Help? ## Need Help
For detailed documentation, see: For detailed documentation, see:
- $env.TOOLS_PATH/dev-system/ci/docs/configuration-guide.md - $env.TOOLS_PATH/dev-system/ci/docs/configuration-guide.md

View File

@ -1,43 +0,0 @@
# .ontoref/
Runtime configuration for the `ontoref-daemon`.
## Registry
`registry.toml` is **derived** — do not edit it by hand.
The source of truth is `registry.ncl`, which carries a typed Nickel schema:
| Field | Type | Notes |
|-------|------|-------|
| `slug` | `String` | Unique project identifier |
| `root` | `String` | Absolute path to the project root |
| `keys` | `Array KeyEntry` | Optional. Each entry: `role` (`admin`\|`viewer`) + `hash` (Argon2id PHC) |
### Why not validate paths in Nickel
Nickel is a pure configuration language — no filesystem access.
Path existence is validated by the generator script at write time, not by the schema.
The schema enforces structure and types; the script enforces runtime semantics.
### Generating registry.toml
```sh
nu .ontoref/gen-registry.nu
# or via just:
just gen-registry
```
Projects whose `root` path does not exist are warned and skipped.
This is intentional: the registry may reference projects present on some machines but not others.
### Adding a key (HTTP auth)
```sh
# 1. Generate the hash
ontoref-daemon --hash-password <your-password>
# 2. Add the entry to registry.ncl under the target project's keys array
# 3. Regenerate
just gen-registry
```

View File

@ -11,10 +11,10 @@
log = { log = {
level = "info", level = "info",
path = ".ontoref/logs", path = "logs",
rotation = "daily", rotation = "daily",
compress = false, compress = false,
archive = ".ontoref/logs-archive", archive = "logs-archive",
max_files = 7, max_files = 7,
}, },

View File

@ -1,35 +0,0 @@
#!/usr/bin/env nu
# .ontoref/gen-registry.nu
# Reads registry.ncl, validates project paths, and writes registry.toml.
# registry.ncl is the source of truth; registry.toml is derived — do not edit it by hand.
def main [] {
let script_dir = ($env.CURRENT_FILE | path dirname)
let ncl_file = $"($script_dir)/registry.ncl"
let toml_file = $"($script_dir)/registry.toml"
let result = (do { ^nickel export --format json $ncl_file } | complete)
if $result.exit_code != 0 {
error make { msg: $"nickel export failed:\n($result.stderr)" }
}
let registry = ($result.stdout | from json)
let valid_projects = ($registry.projects | each { |p|
if ($p.root | path exists) {
$p
} else {
print $" (ansi yellow)WARN(ansi reset) project '($p.slug)': root not found at ($p.root) — skipping"
null
}
} | compact)
let skipped = ($registry.projects | length) - ($valid_projects | length)
if $skipped > 0 {
print $" (ansi yellow)($skipped) project(s) skipped due to missing paths(ansi reset)"
}
{ projects: $valid_projects } | to toml | save -f $toml_file
print $" (ansi green)OK(ansi reset) ($valid_projects | length) project\(s\) written to ($toml_file)"
}

View File

@ -1,41 +0,0 @@
{"ts":"2026-03-12T02:15:07+0000","author":"unknown","actor":"developer","level":"read","action":"adr list"}
{"ts":"2026-03-12T02:58:09+0000","author":"unknown","actor":"developer","level":"read","action":"adr list"}
{"ts":"2026-03-12T03:04:52+0000","author":"unknown","actor":"developer","level":"read","action":"adr list"}
{"ts":"2026-03-12T03:05:21+0000","author":"unknown","actor":"developer","level":"read","action":"adr list"}
{"ts":"2026-03-12T03:05:58+0000","author":"unknown","actor":"developer","level":"read","action":"adr list"}
{"ts":"2026-03-12T03:06:03+0000","author":"unknown","actor":"developer","level":"read","action":"adr list"}
{"ts":"2026-03-12T03:11:03+0000","author":"unknown","actor":"agent","level":"read","action":"adr list"}
{"ts":"2026-03-12T03:12:05+0000","author":"unknown","actor":"developer","level":"read","action":"adr list"}
{"ts":"2026-03-12T03:12:19+0000","author":"unknown","actor":"developer","level":"read","action":"adr list"}
{"ts":"2026-03-12T03:47:17+0000","author":"unknown","actor":"developer","level":"read","action":"adr list"}
{"ts":"2026-03-12T06:41:52+0000","author":"unknown","actor":"developer","level":"read","action":"adr list"}
{"ts":"2026-03-12T06:41:59+0000","author":"unknown","actor":"developer","level":"read","action":"adr list"}
{"ts":"2026-03-12T08:50:18+0000","author":"unknown","actor":"developer","level":"read","action":"constraint"}
{"ts":"2026-03-12T08:51:26+0000","author":"unknown","actor":"developer","level":"read","action":"overview"}
{"ts":"2026-03-12T08:52:04+0000","author":"unknown","actor":"developer","level":"read","action":"about"}
{"ts":"2026-03-12T08:52:23+0000","author":"unknown","actor":"developer","level":"read","action":"about"}
{"ts":"2026-03-12T08:52:44+0000","author":"unknown","actor":"developer","level":"read","action":"status"}
{"ts":"2026-03-12T10:20:47+0000","author":"unknown","actor":"developer","level":"read","action":"describe project"}
{"ts":"2026-03-12T18:29:08+0000","author":"unknown","actor":"developer","level":"read","action":"sync scan"}
{"ts":"2026-03-12T18:33:17+0000","author":"unknown","actor":"developer","level":"read","action":"sync diff"}
{"ts":"2026-03-12T18:33:32+0000","author":"unknown","actor":"developer","level":"read","action":"sync scan"}
{"ts":"2026-03-12T18:33:36+0000","author":"unknown","actor":"developer","level":"read","action":"sync diff"}
{"ts":"2026-03-12T18:39:22+0000","author":"unknown","actor":"developer","level":"read","action":"sync scan"}
{"ts":"2026-03-12T18:39:26+0000","author":"unknown","actor":"developer","level":"read","action":"sync diff"}
{"ts":"2026-03-12T18:39:41+0000","author":"unknown","actor":"developer","level":"read","action":"sync scan"}
{"ts":"2026-03-12T18:39:45+0000","author":"unknown","actor":"developer","level":"read","action":"sync diff"}
{"ts":"2026-03-12T18:40:33+0000","author":"unknown","actor":"developer","level":"read","action":"sync scan"}
{"ts":"2026-03-12T18:40:37+0000","author":"unknown","actor":"developer","level":"read","action":"sync diff"}
{"ts":"2026-03-12T18:41:04+0000","author":"unknown","actor":"developer","level":"read","action":"sync scan"}
{"ts":"2026-03-12T18:41:07+0000","author":"unknown","actor":"developer","level":"read","action":"sync diff"}
{"ts":"2026-03-12T18:43:22+0000","author":"unknown","actor":"developer","level":"read","action":"sync scan"}
{"ts":"2026-03-12T18:43:25+0000","author":"unknown","actor":"developer","level":"read","action":"sync diff"}
{"ts":"2026-03-12T18:43:43+0000","author":"unknown","actor":"developer","level":"read","action":"sync scan"}
{"ts":"2026-03-12T18:43:47+0000","author":"unknown","actor":"developer","level":"read","action":"sync diff"}
{"ts":"2026-03-12T20:46:07+0000","author":"unknown","actor":"developer","level":"read","action":"sync scan"}
{"ts":"2026-03-12T20:46:12+0000","author":"unknown","actor":"developer","level":"read","action":"sync diff"}
{"ts":"2026-03-12T20:47:05+0000","author":"unknown","actor":"developer","level":"read","action":"sync scan"}
{"ts":"2026-03-12T20:47:09+0000","author":"unknown","actor":"developer","level":"read","action":"sync diff"}
test
{"ts":"2026-03-13T00:39:33+0000","author":"unknown","actor":"agent","level":"read","action":"adr list"}
{"ts":"2026-03-13T00:40:00+0000","author":"unknown","actor":"developer","level":"read","action":"adr list"}

8
.ontoref/project.ncl Normal file
View File

@ -0,0 +1,8 @@
let s = import "ontoref-project.ncl" in
s.make_project {
slug = "ontoref",
root = "/Users/Akasha/Development/ontoref",
nickel_import_paths = ["/Users/Akasha/Development/ontoref"],
keys = [],
}

View File

@ -1,33 +0,0 @@
# Registry source of truth — consumed by gen-registry.nu to produce registry.toml.
# Path existence is validated at generation time (Nickel is pure; no filesystem access).
# Hash values are Argon2id PHC strings produced by: ontoref-daemon --hash-password <pw>
let KeyRole = [| 'admin, 'viewer |] in
let KeyEntry = {
role | KeyRole,
hash | String,
} in
let ProjectEntry = {
slug | String,
root | String,
keys | Array KeyEntry | default = [],
} in
{
projects | Array ProjectEntry = [
{
slug = "ontoref",
root = "/Users/Akasha/Development/ontoref",
},
{
slug = "typedialog",
root = "/Users/Akasha/Development/typedialog",
},
{
slug = "stratumiops",
root = "/Users/Akasha/Development/stratumiops",
},
],
}

View File

@ -1,14 +0,0 @@
[[projects]]
keys = []
root = "/Users/Akasha/Development/ontoref"
slug = "ontoref"
[[projects]]
keys = []
root = "/Users/Akasha/Development/typedialog"
slug = "typedialog"
[[projects]]
keys = []
root = "/Users/Akasha/Development/stratumiops"
slug = "stratumiops"

View File

@ -30,14 +30,14 @@ repos:
language: system language: system
types: [rust] types: [rust]
pass_filenames: false pass_filenames: false
stages: [pre-push] stages: [manual]
- id: cargo-deny - id: cargo-deny
name: Cargo deny (licenses & advisories) name: Cargo deny (licenses & advisories)
entry: bash -c 'cargo deny check licenses advisories' entry: bash -c 'cargo deny check licenses advisories'
language: system language: system
pass_filenames: false pass_filenames: false
stages: [pre-push] stages: [manual]
# ============================================================================ # ============================================================================
# Nushell Hooks (optional - enable if using Nushell) # Nushell Hooks (optional - enable if using Nushell)

View File

@ -7,6 +7,169 @@ ADRs referenced below live in `adrs/` as typed Nickel records.
## [Unreleased] ## [Unreleased]
### Auth & Session Model (ADR-005)
Unified key-to-session token exchange across all surfaces. All work gated on `#[cfg(feature = "ui")]`.
- `KeyEntry` gains `label: String` (`#[serde(default)]`) — audit trail for key-based sessions.
NCL schema `install/resources/schemas/ontoref-project.ncl` updated accordingly.
- `verify_keys_list` returns `Option<KeyMatch { role, label }>` instead of `Option<Role>`.
- `SessionEntry` gains `id: String` — stable public identifier distinct from the bearer token,
safe to expose in list responses. Prevents session enumeration by admins.
- `SessionStore` gains secondary `id_index: DashMap<id, token>` for O(1) `revoke_by_id`.
- New `SessionStore` methods: `list_for_slug`, `list_all`, `revoke_all_for_slug`,
`revoke_by_id(id, acting_slug, acting_role) -> RevokeResult`.
- `POST /sessions` — key → UUID v4 token exchange. Accepts project keys or daemon admin password
(`project: "_daemon"`). Rate-limited. No authentication required (it is the credential exchange endpoint).
- `GET /sessions?project=slug` — list active sessions for a project (viewer+). Without `?project=`
param requires daemon admin and returns all sessions.
- `DELETE /sessions/{id}` — revoke by public id. Project admin: own project only. Daemon admin: any.
- `require_session()` helper: validates UUID v4 Bearer → `SessionEntry`, error boxed (`Box<Response>`)
to satisfy `clippy::result_large_err`.
- `check_primary_auth` fast-path: UUID v4 bearer → session lookup (O(1)) before argon2 fallback (~100ms).
- `project_update_keys` (`PUT /projects/{slug}/keys`) now calls `sessions.revoke_all_for_slug` in
addition to actor deregistration. All in-flight UI sessions for the rotated project are immediately
invalidated.
- Daemon admin: `ONTOREF_ADMIN_TOKEN_FILE` (preferred — hash not visible in `ps aux`) or
`ONTOREF_ADMIN_TOKEN`. Sessions use virtual slug `"_daemon"`.
- `manage_login_page` / `manage_login_submit` / `manage_logout` handlers for `/ui/manage/login`
and `/ui/manage/logout`.
- `AdminGuard` redirects to `/ui/manage/login` when `daemon_admin_hash` is set.
### CLI Bearer Token
- `bearer-args` exported from `reflection/modules/store.nu`: returns `["-H" "Authorization: Bearer $token"]`
when `ONTOREF_TOKEN` is set, `[]` otherwise.
- `http-get`, `http-post-json`, `http-delete` (new) in `store.nu` use `...$auth` — Bearer injected
transparently, no behavior change when `ONTOREF_TOKEN` is unset.
- `notify-daemon-project-add` and `notify-daemon-project-remove` in `reflection/bin/ontoref.nu`
use `bearer-args`.
### Project Setup & Onboarding
- `ontoref setup` is now the primary onboarding command (replaces manual `cp templates/` pattern).
- `--kind <K>` flag: `Service` (default) | `Library` | `DevWorkspace` | `PublishedCrate` |
`AgentResource` | `Mixed`.
- `--parent <path>` flag: generates manifest with `implementation` layer + `<slug>-framework` layer
and `<slug>-browse` op mode for implementation children.
- Logo auto-detection: `setup` scans `assets/` for `<slug>-logo.svg`, `<slug>.svg`, `logo.svg`
(and `.png` variants); inserts `ui.logo` into generated `config.ncl` when found.
- `--gen-keys ["admin:label" "viewer:label"]` flag: idempotent bootstrap — skips if `role =` already
present in `project.ncl`. Hashes via `ontoref-daemon.bin --hash-password`; prints passwords once
to stdout.
- All `mkdir` calls in setup guarded by `if not (path | path exists)`.
### Self-Description — on+re Update
`.ontology/core.ncl` — 3 new Practice nodes, updated `ontoref-daemon` description and `artifact_paths`:
| Node | Pole | Description |
| --- | --- | --- |
| `unified-auth-model` | Yang | Key→session token exchange; session.id ≠ bearer; revoke on key rotation |
| `project-onboarding` | Yang | `ontoref setup` — idempotent scaffold, `--kind`, `--parent`, `--gen-keys` |
`ontoref-daemon` node updated: auth/session management added to description and `artifact_paths`
(`session.rs`, `auth.rs`, `login.rs`).
New edges: `unified-auth-model → ontoref-daemon`, `unified-auth-model → no-enforcement`
(Contradicts/Low — auth is opt-in), `ontoref-daemon → unified-auth-model` (Contains),
`project-onboarding → unified-auth-model` (DependsOn),
`project-onboarding → adopt-ontoref-tooling` (Complements),
`project-onboarding → daemon-config-management` (DependsOn).
`.ontology/state.ncl``self-description-coverage` transitions to `current_state = "fully-self-described"`.
Blocker resolved: `reflection/backlog.ncl` created, ADR-005 recorded.
`reflection/backlog.ncl` — created with 6 items: bl-001 (ontoref.dev), bl-002 (first external project),
bl-003 (`ontoref keys` CLI), bl-004 (session UI views), bl-005 (Syntaxis migration),
bl-006 (ADR-001 acceptance).
Previous state: 4 axioms, 2 tensions, 13 practices. Current: 4 axioms, 2 tensions, 15 practices.
### Protocol
- ADR-004 accepted: NCL pipe bootstrap pattern —
`nickel export config.ncl | ontoref-daemon.bin --config-stdin`.
Stages: Nickel evaluation → optional SOPS/Vault merge → binary via stdin.
([adr-004](adrs/adr-004-ncl-pipe-bootstrap-pattern.ncl))
- ADR-005 accepted: unified key-to-session auth model across CLI, UI, and MCP. Opaque UUID v4 tokens,
`session.id ≠ bearer`, revocation on key rotation, daemon admin via `_daemon` slug.
([adr-005](adrs/adr-005-unified-auth-session-model.ncl))
---
### Install Infrastructure
- `install/` directory reorganized: binaries, bootstrapper, global CLI, resources, and validation
scripts co-located.
- Binary installed as `ontoref-daemon.bin`; public entrypoint bootstrapper installed as `ontoref-daemon`.
Users never call `.bin` directly.
- `install/ontoref-daemon-boot` (renamed from `ontoref-daemon-start`) — NCL pipe bootstrapper
implementing ADR-004. Stages: `nickel export config.ncl` → optional SOPS/Vault secret merge →
`ontoref-daemon.bin --config-stdin`. Supports `--dry-run`, `--sops`, `--vault`.
- `install/ontoref-daemon-boot` sets `NICKEL_IMPORT_PATH` (config dir + platform data dir schemas)
and `NATS_STREAMS_CONFIG` (default `~/.config/ontoref/streams.json`) before launching the binary.
- `install/install.nu` — installs binary, bootstrapper, global `ontoref` CLI (ONTOREF_ROOT baked in
at install time), UI assets, config skeleton, and global NATS topology.
Hash-checked — unchanged files are not overwritten.
- `install/config-setup.nu` — standalone validation script: `nickel typecheck` + `nickel export`,
path existence checks, DB and NATS liveness probes (`nc -z -w2`).
- `install/check-config-sync.nu` — CI guard asserting that every `nickel_path`-bearing field in
`reflection/forms/config.ncl` has a matching `{{ name }}` reference in `reflection/forms/config.ncl.j2`,
and vice versa. Wired into `just ci-lint` and `just ci-full`.
### Config Management
- `install/resources/config.ncl` — default global config skeleton with full Nickel contracts
(`Port`, `LogLevel`, `Rotation`, `Actor`, `Severity`). Covers: daemon, db, nats_events, log,
cache, ui, mode_run, actor_init, quick_actions, nickel_import_paths.
- `install/resources/streams.json` — global default NATS JetStream topology: ECOSYSTEM stream
(`ecosystem.>`, 30-day retention), no project-specific consumers.
Installed to `~/.config/ontoref/streams.json`.
- `nats/streams.json` — ontoref project-local topology with `daemon-ontoref` and `cli-notifications`
consumers on ECOSYSTEM stream.
- `reflection/forms/config.ncl` + `reflection/forms/config.ncl.j2` — typedialog roundtrip for
browser-based config editing (`ontoref config-edit`). Form populates fields from existing config
via `nickel_path`; Tera template reconstructs full NCL with all contracts on save.
- `reflection/nulib/bootstrap.nu` — Nu bootstrapper helper updated: `nats-streams-config` function
resolves `NATS_STREAMS_CONFIG` default; env var passed to daemon process via `with-env`.
- Daemon `nats.rs`: empty `streams_config` string → `None`, activating `TopologyConfig::load` fallback
to `NATS_STREAMS_CONFIG` env var. Projects with a local `nats/streams.json` set `streams_config`
explicitly in their config.
### Daemon Fixes
- `--config-stdin` now exclusively skips `.ontoref/config.ncl` — project config is never loaded when
stdin config is active. Previously both paths could run.
- DB connection (`stratum-db`) only established when `db.enabled = true` in config. Previously
connected regardless of `enabled` flag.
- NATS connection (`platform-nats`) only established when `nats_events.enabled = true`.
"Connecting to NATS..." log moved after the enabled check.
- `NatsPublisher::connect` signature changed from `config_path: &PathBuf` (re-read file) to
`config: Option<&serde_json::Value>` (already-loaded JSON). Eliminates double file read and ensures
NATS uses the same config source as the rest of the daemon.
- `load_config_overrides` returns `(Option<String>, Option<serde_json::Value>)` — nickel import path
and parsed config JSON returned together. `apply_stdin_config` returns `serde_json::Value` directly.
### Self-Description — on+re Update
`.ontology/core.ncl` — 1 new Practice node, updated `ontoref-daemon` description and artifact_paths:
| Node | Pole | Description |
| --- | --- | --- |
| `daemon-config-management` | Yang | Install + config form/template roundtrip, CI guard, global NATS topology, config-setup validation |
New edges: `daemon-config-management → ontoref-daemon` (DependsOn),
`daemon-config-management → adopt-ontoref-tooling` (Complements).
`.ontology/state.ncl``operational-mode` dimension description updated to reference ADR-004 bootstrap
and `NATS_STREAMS_CONFIG` mechanism. `protocol-maturity` blocker updated to reflect install pipeline
completion.
`.ontology/manifest.ncl``tooling` layer paths updated to include `install/` and `nats/`.
Previous state: 4 axioms, 2 tensions, 12 practices. Current: 4 axioms, 2 tensions, 13 practices.
### Protocol ### Protocol
- ADR-001 accepted: ontoref extracted as a standalone protocol project, independent of - ADR-001 accepted: ontoref extracted as a standalone protocol project, independent of
@ -24,21 +187,25 @@ ADRs referenced below live in `adrs/` as typed Nickel records.
### Crates ### Crates
- `ontoref-ontology`: Rust crate for loading `.ontology/*.ncl` as typed structs (`Core`, `Gate`, `State`). Zero stratumiops dependencies. - `ontoref-ontology`: Rust crate for loading `.ontology/*.ncl` as typed structs (`Core`, `Gate`,
- `ontoref-reflection`: Rust crate for loading, validating, and executing Reflection modes as NCL DAG contracts. Optional `nats` feature (path dep: `platform-nats`). `State`). Zero stratumiops dependencies.
- `ontoref-reflection`: Rust crate for loading, validating, and executing Reflection modes as NCL
DAG contracts. Optional `nats` feature (path dep: `platform-nats`).
- `ontoref-daemon`: Rust crate providing HTTP API (axum), DashMap-backed NCL export cache, - `ontoref-daemon`: Rust crate providing HTTP API (axum), DashMap-backed NCL export cache,
notify-based file watcher, and actor registry. Optional `db` feature (path dep: `stratum-db`) notify-based file watcher, and actor registry. Optional `db` feature (path dep: `stratum-db`)
and `nats` feature (path dep: `platform-nats`). and `nats` feature (path dep: `platform-nats`).
### Daemon — Q&A NCL Persistence (`crates/ontoref-daemon/src/ui/qa_ncl.rs`) ### Daemon — Q&A NCL Persistence (`crates/ontoref-daemon/src/ui/qa_ncl.rs`)
Line-level NCL surgery for `reflection/qa.ncl` — same pattern as `backlog_ncl.rs`. No AST parsing, no nickel-lang-core dependency. Line-level NCL surgery for `reflection/qa.ncl` — same pattern as `backlog_ncl.rs`. No AST parsing,
no nickel-lang-core dependency.
- `add_entry` — appends a typed `QaEntry` block before `],` array close; generates sequential `qa-NNN` ids - `add_entry` — appends a typed `QaEntry` block before `],` array close; generates sequential `qa-NNN` ids
- `update_entry` — in-place field mutation via bidirectional scan (question + answer fields) - `update_entry` — in-place field mutation via bidirectional scan (question + answer fields)
- `remove_entry` — removes the full block by id using backward scan for `{` and forward scan for `},` - `remove_entry` — removes the full block by id using backward scan for `{` and forward scan for `},`
HTTP endpoints (all under `#[cfg(feature = "ui")]` except read-only GET): HTTP endpoints (all under `#[cfg(feature = "ui")]` except read-only GET):
- `GET /qa-json` — export all Q&A entries as JSON (read-only, always enabled) - `GET /qa-json` — export all Q&A entries as JSON (read-only, always enabled)
- `POST /qa/add` — append new entry; returns generated id - `POST /qa/add` — append new entry; returns generated id
- `POST /qa/delete` — remove entry by id; invalidates NCL cache - `POST /qa/delete` — remove entry by id; invalidates NCL cache
@ -59,7 +226,8 @@ Four new MCP tools exposed to AI agents:
| `ontoref_action_list` | List all quick actions from `.ontoref/config.ncl` export. | | `ontoref_action_list` | List all quick actions from `.ontoref/config.ncl` export. |
| `ontoref_action_add` | Create a new reflection mode at `reflection/modes/<id>.ncl` and register it as a quick action. | | `ontoref_action_add` | Create a new reflection mode at `reflection/modes/<id>.ncl` and register it as a quick action. |
Constraint: `ontoref_qa_list` and `ontoref_qa_add` never trigger `apply` steps or modify `.ontology/` files (enforced by ADR-003). Constraint: `ontoref_qa_list` and `ontoref_qa_add` never trigger `apply` steps or modify `.ontology/`
files (enforced by ADR-003).
### Daemon — Passive Drift Observation (`crates/ontoref-daemon/src/ui/drift_watcher.rs`) ### Daemon — Passive Drift Observation (`crates/ontoref-daemon/src/ui/drift_watcher.rs`)
@ -76,11 +244,14 @@ Started from `main.rs` under `#[cfg(feature = "ui")]`; failure to start is non-f
### Tooling ### Tooling
- `reflection/modules/`: 16 Nushell operational modules (`adr.nu`, `backlog.nu`, `coder.nu`, `describe.nu`, `sync.nu`, etc.) - `reflection/modules/`: 16 Nushell operational modules (`adr.nu`, `backlog.nu`, `coder.nu`,
- `reflection/modes/`: 10 NCL DAG operational modes including `adopt_ontoref`, `sync-ontology`, `coder-workflow`, `create-pr` `describe.nu`, `sync.nu`, etc.)
- `reflection/modes/`: 10 NCL DAG operational modes including `adopt_ontoref`, `sync-ontology`,
`coder-workflow`, `create-pr`
- `reflection/forms/`: 7 interactive NCL forms for ADR lifecycle, backlog, and adoption - `reflection/forms/`: 7 interactive NCL forms for ADR lifecycle, backlog, and adoption
- `templates/`: Consumer-facing adoption templates (`ontoref-config.ncl`, `ontology/`, `scripts-ontoref`) - `templates/`: Consumer-facing adoption templates (`ontoref-config.ncl`, `ontology/`, `scripts-ontoref`)
- `./ontoref`: Bash entry point with actor auto-detection, advisory file locking, and Nushell version guard (>= 0.110.0) - `./ontoref`: Bash entry point with actor auto-detection, advisory file locking, and Nushell version
guard (>= 0.110.0)
### Self-Description — on+re Update ### Self-Description — on+re Update
@ -100,7 +271,8 @@ New edges: `qa-knowledge-store → dag-formalized`, `qa-knowledge-store → code
Previous state: 4 axioms, 2 tensions, 9 practices. Current: 4 axioms, 2 tensions, 12 practices. Previous state: 4 axioms, 2 tensions, 9 practices. Current: 4 axioms, 2 tensions, 12 practices.
`reflection/schemas/qa.ncl``QaStore` and `QaEntry` types (id, question, answer, actor, created_at, tags, related, verified). `reflection/schemas/qa.ncl``QaStore` and `QaEntry` types (id, question, answer, actor, created_at,
tags, related, verified).
`reflection/qa.ncl` — typed store file, conforms to `QaStore` contract, Nickel typecheck must pass. `reflection/qa.ncl` — typed store file, conforms to `QaStore` contract, Nickel typecheck must pass.
--- ---

3
Cargo.lock generated
View File

@ -2891,8 +2891,10 @@ dependencies = [
"tera", "tera",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio", "tokio",
"tokio-stream",
"tokio-test", "tokio-test",
"toml", "toml",
"tower",
"tower-http", "tower-http",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@ -5238,6 +5240,7 @@ dependencies = [
"futures-core", "futures-core",
"pin-project-lite", "pin-project-lite",
"tokio", "tokio",
"tokio-util",
] ]
[[package]] [[package]]

View File

@ -44,6 +44,13 @@ subprocess when unavailable.
### Daemon Capabilities ### Daemon Capabilities
**Unified Auth Model** — all surfaces (CLI, UI, MCP) exchange a key for a UUID v4 session token
via `POST /sessions`. Token lifetime: 30 days, O(1) lookup. Project keys carry `role`
(admin|viewer) and `label` for audit trail. Daemon-level admin via `ONTOREF_ADMIN_TOKEN_FILE`.
`GET /sessions` and `DELETE /sessions/{id}` for session visibility and revocation. Key rotation
invalidates all sessions for the rotated project. CLI injects `ONTOREF_TOKEN` as Bearer
automatically.
**Q&A Knowledge Store** — accumulated Q&A entries persist to `reflection/qa.ncl` (typed NCL, **Q&A Knowledge Store** — accumulated Q&A entries persist to `reflection/qa.ncl` (typed NCL,
git-versioned). Not localStorage. Any actor — developer, agent, CI — reads the same store. git-versioned). Not localStorage. Any actor — developer, agent, CI — reads the same store.
@ -67,27 +74,48 @@ After a 15s debounce runs `sync scan + sync diff`; emits an `ontology_drift` not
MISSING/STALE/DRIFT/BROKEN items are found. Never applies changes — `apply` is always deliberate. MISSING/STALE/DRIFT/BROKEN items are found. Never applies changes — `apply` is always deliberate.
**Quick Actions** — runnable shortcuts over reflection modes, configured as `quick_actions` in **Quick Actions** — runnable shortcuts over reflection modes, configured as `quick_actions` in
`.ontoref/config.ncl`. Accessible from HTTP (`/actions`), CLI (`./ontoref`), and MCP `.ontoref/config.ncl`. Accessible from HTTP (`/actions`), CLI (`ontoref`), and MCP
(`ontoref_action_list/add`). (`ontoref_action_list/add`).
## Adopting ontoref ## Install
A consumer project needs exactly two artifacts:
```sh ```sh
# 1. Copy the entry point wrapper just install-daemon # build + install binary, bootstrapper, CLI, UI assets, config skeleton
cp templates/scripts-ontoref scripts/ontoref ontoref config-edit # browser form → ~/.config/ontoref/config.ncl
chmod +x scripts/ontoref ontoref-daemon-boot # NCL pipe bootstrap: nickel export config.ncl | daemon --config-stdin
ontoref-daemon-boot --dry-run # preview composed JSON without starting
# 2. Initialize config
mkdir -p .ontoref
cp templates/ontoref-config.ncl .ontoref/config.ncl
``` ```
Or run the adoption mode interactively: Installed layout (`~/.local/bin/`):
| Binary | Role |
| --- | --- |
| `ontoref` | Global CLI dispatcher — all reflection modes, ADR lifecycle, daemon control |
| `ontoref-daemon` | Bootstrapper (public entrypoint) — validates config via Nickel, pipes JSON to binary |
| `ontoref-daemon.bin` | Compiled Rust binary — never called directly |
Global config at `~/.config/ontoref/config.ncl` (type-checked Nickel). Global NATS stream topology at
`~/.config/ontoref/streams.json`. Project-local topology override via `nats/streams.json` +
`nats_events.streams_config` in `.ontoref/config.ncl`.
## Onboarding a project
```sh ```sh
./ontoref --actor developer adopt_ontoref cd /path/to/my-project
ontoref setup # idempotent; kind: Service by default
ontoref setup --kind Library # Library | DevWorkspace | PublishedCrate | AgentResource | Mixed
ontoref setup --parent /path/to/fw # implementation child: adds framework layer + browse mode
ontoref setup --gen-keys ["admin:dev" "viewer:ci"] # bootstrap auth keys (no-op if keys already exist)
```
`ontoref setup` creates `.ontoref/project.ncl`, `.ontoref/config.ncl` (with logo auto-detection),
`.ontology/` scaffold, `adrs/`, `reflection/modes/`, `backlog.ncl`, `qa.ncl`, git hooks, and
registers the project in `~/.config/ontoref/projects.ncl`.
For existing projects that predate `setup`, the adoption mode is still available:
```sh
ontoref --actor developer adopt_ontoref
``` ```
`ONTOREF_PROJECT_ROOT` is set by the consumer wrapper — one ontoref checkout serves multiple projects. `ONTOREF_PROJECT_ROOT` is set by the consumer wrapper — one ontoref checkout serves multiple projects.

View File

@ -0,0 +1,113 @@
let d = import "defaults.ncl" in
d.make_adr {
id = "adr-004",
title = "NCL Pipe Bootstrap — Config Validation and Secret Injection via Unix Pipeline",
status = 'Accepted,
date = "2026-03-13",
context = "Ontoref-daemon and any process that receives structured config faces two problems: (1) Nickel NCL requires a subprocess to evaluate, introducing a system-call injection surface if the daemon itself calls `nickel export` at runtime; (2) credentials and secrets embedded in config files (TOML, JSON) persist on disk after the process starts, creating a forensic artifact. The existing registry.toml approach (NCL → TOML file → daemon reads file) partially addresses the first problem but not the second — the TOML file remains on disk with hashed credentials. SOPS and Vault are standard secret management tools that produce decrypted output on stdout.",
decision = "All config delivery to long-running processes follows a three-stage Unix pipeline: Stage 1 — structural validation: `nickel export --format json config.ncl` produces JSON with schema-validated structure but no secret values; Stage 2 — secret injection (optional): SOPS decrypt or Vault lookup merges credentials into the JSON stream; Stage 3 — process bootstrap: the target process reads the composed JSON from stdin via `--config-stdin`. No intermediate file is written to disk. If any stage fails, the pipeline breaks and the process does not start. A bash wrapper script (not Nu — Nu may not be available at service boot time) orchestrates the pipeline. A Nu helper `ncl-bootstrap` provides the same interface for interactive/development use.",
rationale = [
{
claim = "Pipe eliminates disk artifacts for secrets",
detail = "Pipe contents are kernel-managed memory, inaccessible to other processes, and ephemeral — no filesystem metadata, no tmpfs entry, nothing survives process termination. A TOML or JSON file on disk persists until explicitly deleted, is accessible to any process running as the same UID, and may appear in filesystem audit logs. For secrets (DB passwords, API keys, Argon2id hashes), the pipe is materially safer.",
},
{
claim = "Nickel is the validation layer, not the runtime config format",
detail = "The daemon never calls `nickel export` — it only reads JSON from stdin. The NCL schema enforces structural correctness and type safety before the process starts. This separates concerns: NCL for authoring and validation, JSON for delivery. No Nickel subprocess risk at runtime.",
},
{
claim = "Bash wrapper for service launchers, Nu helper for development",
detail = "System service managers (launchctl, systemd) run in environments where Nu may not be on PATH or may be a different version. The bash wrapper has zero dependencies beyond bash, nickel, and the target binary. The Nu ncl-bootstrap helper provides richer error messages and structured output for interactive development use. Both implement the same pipeline.",
},
{
claim = "SOPS and Vault integrate as a composable pipeline stage",
detail = "Stage 2 is optional and replaceable. With SOPS: `sops --decrypt secrets.enc.json`. With Vault: `vault kv get -format=json secret/path | jq '.data.data'`. Both produce JSON on stdout. `jq -s '.[0] * .[1]'` merges the structural config with the secrets. The NCL file never contains secret values — only SecretRef placeholders if self-documentation is desired.",
},
{
claim = "Pipeline failure semantics are correct by default",
detail = "If `nickel export` fails (schema violation, syntax error), stdout is empty or truncated — the target process receives invalid JSON and fails at parse time, before any initialization. If SOPS or Vault fails, same result. The process never starts in a partially-configured state. This is safer than file-based config where the process may start with a stale or invalid file.",
},
],
consequences = {
positive = [
"No credentials on disk after process startup — ephemeral pipe only",
"NCL schema violations prevent daemon startup — config errors are caught early",
"SOPS and Vault integrate without changes to the daemon — secrets are a pipeline concern",
"Pattern is reusable: any project can adopt ncl-bootstrap for any long-running process",
"Bash wrapper works in Docker, CI, launchctl, systemd without Nu dependency",
],
negative = [
"Daemon loses ability to hot-reload config from disk — config changes require restart via wrapper",
"stdin is consumed at startup — daemon must redirect stdin to /dev/null after reading config",
"Pipeline debugging is harder than inspecting a config file — need wrapper --dry-run mode",
"Nu must not be required for the bash wrapper — two implementations of the same pattern to maintain",
],
},
alternatives_considered = [
{
option = "TOML file on disk (current registry.toml approach)",
why_rejected = "File persists on disk with credentials. Stale file may be read after config changes. Requires explicit cleanup logic. Forensic artifact risk.",
},
{
option = "Environment variables for secrets",
why_rejected = "Environment variables are visible in /proc/PID/environ on Linux and via `ps eww` on some systems. They persist for the lifetime of the process and are inherited by child processes. Worse attack surface than stdin pipe.",
},
{
option = "Encrypted TOML file (AES256 at rest)",
why_rejected = "Decryption key must be available at runtime — the problem is deferred, not solved. The decrypted form still passes through disk (tmpfs or swap). Adds a custom encryption layer instead of using standard tools (SOPS, Vault) that the ecosystem already supports.",
},
{
option = "Daemon reads NCL directly at runtime via nickel-lang-core",
why_rejected = "nickel-lang-core has an unstable Rust API. More critically, it means the daemon can evaluate arbitrary Nickel — including NCL files with system calls via builtins. The pipeline approach ensures the daemon only ever sees validated JSON, never executable Nickel.",
},
],
constraints = [
{
id = "no-config-file-on-disk",
claim = "The bootstrap pipeline must not write an intermediate config file to disk at any stage",
scope = "scripts/ontoref-daemon-start, reflection/nulib/bootstrap.nu",
severity = 'Hard,
check_hint = "grep -E 'tee|>|tempfile|mktemp' scripts/ontoref-daemon-start",
rationale = "An intermediate file defeats the purpose of the pipeline. If a file is needed for debugging, use --dry-run which prints to stdout only.",
},
{
id = "bash-wrapper-zero-deps",
claim = "The bash wrapper must depend only on bash, nickel, and the target binary — no Nu, no jq unless SOPS/Vault stage is active",
scope = "scripts/ontoref-daemon-start",
severity = 'Hard,
check_hint = "head -5 scripts/ontoref-daemon-start",
rationale = "System service managers may not have Nu on PATH. The wrapper must be portable across launchctl, systemd, Docker entrypoints.",
},
{
id = "config-stdin-closes-after-read",
claim = "The target process must redirect stdin to /dev/null after reading the config JSON",
scope = "crates/ontoref-daemon/src/main.rs",
severity = 'Hard,
check_hint = "rg 'config.stdin\\|/dev/null\\|stdin.*close' crates/ontoref-daemon/src/main.rs",
rationale = "stdin left open blocks terminal interaction and causes confusion in interactive sessions. The daemon is a server — it must not hold stdin.",
},
{
id = "ncl-no-secret-values",
claim = "NCL config files used with ncl-bootstrap must not contain plaintext secret values — only SecretRef placeholders or empty fields",
scope = ".ontoref/config.ncl, APP_SUPPORT/ontoref/config.ncl",
severity = 'Hard,
check_hint = "nickel export .ontoref/config.ncl | jq 'paths(scalars) | select(test(\"password|secret|key|token|hash\"))'",
rationale = "If secrets are in the NCL file, they are readable as plaintext by anyone with filesystem access. Secrets enter the pipeline only at the SOPS/Vault stage.",
},
],
related_adrs = ["adr-002-daemon-for-caching-and-notification-barrier"],
ontology_check = {
decision_string = "NCL pipe bootstrap — config validation via nickel export piped to process stdin, secrets injected via SOPS/Vault as optional pipeline stage, no intermediate files",
invariants_at_risk = ["protocol-not-runtime"],
verdict = 'Safe,
},
}

View File

@ -0,0 +1,126 @@
let d = import "defaults.ncl" in
d.make_adr {
id = "adr-005",
title = "Unified Key-to-Session Auth Model Across CLI, UI, and MCP",
status = 'Accepted,
date = "2026-03-13",
context = "ontoref-daemon exposes project knowledge and mutations over HTTP, a browser UI, and an MCP server. Projects can define argon2id-hashed keys in project.ncl (with role admin|viewer and an audit label). Prior to this ADR, the UI login flow set a session cookie but the REST API accepted raw passwords as Bearer tokens on every request — each call paying ~100ms for argon2 verification. The CLI had no Bearer support at all; project-add and project-remove called the daemon without credentials. The daemon manage page had no admin identity concept. There was no way to enumerate or revoke active sessions.",
decision = "All surfaces exchange a raw key once via POST /sessions for a UUID v4 bearer token (30-day lifetime, in-memory SessionStore). The session token is used for all subsequent calls — O(1) DashMap lookup. Sessions carry a stable public id (distinct from the bearer token) for safe list/revoke operations without leaking credentials. Project keys have a label field for audit trail. Daemon-level admin uses a separate argon2id hash (ONTOREF_ADMIN_TOKEN_FILE preferred over ONTOREF_ADMIN_TOKEN) and creates sessions under virtual slug '_daemon'. The CLI injects ONTOREF_TOKEN as Authorization: Bearer automatically via bearer-args in store.nu. Key rotation (PUT /projects/{slug}/keys) revokes all active sessions for the rotated project. GET /sessions and DELETE /sessions/{id} implement two-tier visibility: project admin sees own project sessions; daemon admin sees all.",
rationale = [
{
claim = "Token exchange eliminates per-request argon2 cost",
detail = "argon2id verification takes ~100ms by design (brute-force resistance). Verifying on every CLI invocation, MCP tool call, or UI AJAX request is prohibitive. A UUID v4 session token turns auth into a DashMap::get — O(1), sub-microsecond. The argon2 cost is paid exactly once per login.",
},
{
claim = "session.id != bearer token prevents session enumeration attacks",
detail = "The list endpoints (GET /sessions) return SessionView with a stable public id. If the bearer token were exposed in list responses, a project admin could steal another admin's token and impersonate them. The id field is a second UUID v4 — safe to expose, useless as a credential.",
},
{
claim = "is_uuid_v4 fast-path preserves backward compatibility",
detail = "check_primary_auth tries the session token path first (UUID v4 format check is a length + byte comparison, ~10ns). If the bearer is not UUID-shaped, it falls through to argon2 password verification. Existing integrations using raw passwords continue to work without change.",
},
{
claim = "Virtual '_daemon' slug isolates admin sessions from project sessions",
detail = "Daemon admin sessions are not scoped to any real project. The '_daemon' slug cannot collide with real project slugs (kebab-case, no leading underscore). AdminGuard checks for Role::Admin across any registered project or '_daemon', giving the manage page a clean auth boundary without coupling it to any specific project.",
},
{
claim = "ONTOREF_ADMIN_TOKEN_FILE preferred over inline env var",
detail = "An inline hash in ONTOREF_ADMIN_TOKEN appears in `ps aux` output and shell history. Reading from a file (ONTOREF_ADMIN_TOKEN_FILE) avoids both surfaces. The file contains only the argon2id hash string; the actual password never touches the daemon process env.",
},
{
claim = "Key rotation invalidates sessions atomically",
detail = "When keys are rotated, all in-flight sessions for that project authenticated against the old key set are immediately invalid. revoke_all_for_slug scans the DashMap and removes matching entries including the id_index. Actor sessions (ontoref-daemon actors registry) are also invalidated. The UI will receive 401 on next request and redirect to login.",
},
],
consequences = {
positive = [
"REST API, MCP, and UI all use the same session token — single auth model, no surface-specific logic",
"CLI project-add, project-remove, and notify-daemon-* calls carry credentials automatically when ONTOREF_TOKEN is set",
"Session list gives admins visibility into who is connected to their project",
"Revocation is O(1) via id_index secondary index; bulk revocation on key rotation is O(active sessions for slug)",
"Daemon admin and project admin are cleanly separated — '_daemon' sessions cannot access project-scoped endpoints and vice versa",
],
negative = [
"Sessions are in-memory only — daemon restart requires all clients to re-authenticate",
"30-day token lifetime means a leaked token is valid for up to 30 days unless explicitly revoked",
"ONTOREF_TOKEN in shell env is visible to child processes — use a secrets manager or short-lived token refresh for automated pipelines",
],
},
alternatives_considered = [
{
option = "Verify argon2 on every Bearer request (no session concept)",
why_rejected = "~100ms per request is unacceptable for CLI invocations (each command is a new HTTP call) and MCP tool sequences (multiple calls per agent turn). Session token lookup is sub-microsecond.",
},
{
option = "Axum middleware for auth instead of per-handler check_primary_auth",
why_rejected = "Middleware applies uniformly to all routes. ontoref-daemon coexists auth-enabled and open projects on the same router — some endpoints are always public (health, POST /sessions itself), some require project-scoped auth, some require daemon admin. Per-handler checks encode these rules explicitly; middleware would require complex route exemption logic.",
},
{
option = "Expose bearer token in session list responses",
why_rejected = "GET /sessions is accessible to project admins. Exposing the bearer would allow any project admin to impersonate any other session holder. The public session.id is a safe substitute for revocation targeting.",
},
{
option = "Separate token store per project",
why_rejected = "A single DashMap keyed by token with a slug field in SessionEntry is sufficient. A secondary id_index DashMap gives O(1) revoke-by-id. Per-project sharding would add complexity without benefit given the expected session count (tens, not millions).",
},
{
option = "JWT instead of opaque UUID v4 tokens",
why_rejected = "JWTs are self-contained and cannot be revoked without a denylist. Opaque tokens enable instant revocation (key rotation, logout, admin force-revoke) with O(1) lookup. The daemon is local-only — there is no distributed verification scenario that would justify JWT complexity.",
},
],
constraints = [
{
id = "session-token-never-in-list-response",
claim = "GET /sessions responses must never include the bearer token, only the public session id",
scope = "crates/ontoref-daemon/src/session.rs, crates/ontoref-daemon/src/api.rs",
severity = 'Hard,
check_hint = "rg 'SessionView' crates/ontoref-daemon/src/session.rs — verify no 'token' field exists in SessionView",
rationale = "Exposing bearer tokens in list responses would allow admins to impersonate other sessions. The session.id field is a second UUID v4, safe to expose.",
},
{
id = "post-sessions-no-auth-required",
claim = "POST /sessions must not require authentication — it is the credential exchange endpoint",
scope = "crates/ontoref-daemon/src/api.rs",
severity = 'Hard,
check_hint = "rg -A5 'route.*sessions.*post' crates/ontoref-daemon/src/api.rs — must not call require_session or check_primary_auth",
rationale = "Requiring auth to obtain auth is a bootstrap deadlock. Rate-limiting on failure is the correct mitigation, not pre-authentication.",
},
{
id = "key-rotation-must-invalidate-sessions",
claim = "PUT /projects/{slug}/keys must call revoke_all_for_slug before persisting new keys",
scope = "crates/ontoref-daemon/src/api.rs",
severity = 'Hard,
check_hint = "rg 'revoke_all_for_slug' crates/ontoref-daemon/src/api.rs — must appear in project_update_keys handler",
rationale = "Sessions authenticated against the old key set become invalid after rotation. Failing to revoke them would leave stale sessions with elevated access.",
},
{
id = "bearer-args-injected-by-cli",
claim = "All CLI HTTP calls to the daemon must use bearer-args from store.nu — no hardcoded curl without auth args",
scope = "reflection/modules/store.nu, reflection/bin/ontoref.nu",
severity = 'Soft,
check_hint = "rg 'curl -sf' reflection/bin/ontoref.nu — every occurrence should use ...(bearer-args) or http-get/http-post-json/http-delete helpers",
rationale = "ONTOREF_TOKEN is the single credential source for CLI. Direct curl without bearer-args bypasses the auth model silently.",
},
],
related_adrs = ["adr-002-daemon-for-caching-and-notification-barrier"],
ontology_check = {
decision_string = "unified key-to-session token exchange; opaque UUID v4 bearer; session.id != token; revocation on key rotation",
invariants_at_risk = ["no-enforcement"],
verdict = 'RequiresJustification,
},
invariant_justification = {
invariant = "no-enforcement",
claim = "Auth is opt-in per project. A project with no keys configured runs in open mode — all endpoints accessible without credentials. The protocol itself never mandates auth.",
mitigation = "The no-enforcement axiom applies at the protocol level. Auth is a project-level operational decision, not a protocol constraint. Open deployments (no keys) pass through all auth checks unconditionally.",
},
}

119
assets/logo_prompt.md Normal file
View File

@ -0,0 +1,119 @@
Design a minimalist technical logo for a protocol project called “Ontoref”.
Ontoref is a self-describing ontology and reflection protocol for evolving codebases.
It models how software projects describe their structure and observe their own evolution using directed acyclic graphs (DAGs).
Tagline:
“Structure that remembers why.”
Concept to visualize
The logo should express a balanced duality between:
Ontology
• structure
• nodes
• invariants
• static form
• what is
Reflection
• flow
• edges
• transitions
• evolution
• what becomes
The symbol should feel like structure and reflection at the same time.
Examples of visual metaphors:
• a graph node that mirrors itself
• a minimal DAG structure with reflective symmetry
• a directed edge suggesting motion and observation
• negative space completing the structure
The mark should imply a system that observes itself.
Style
• minimalist
• geometric
• flat vector logo
• strong silhouette
• balanced symmetry
• technical and precise
• no decorative elements
• readable at small sizes (1632px)
The logo must feel like infrastructure software, not a startup brand.
Symbol constraints
• abstract DAG-inspired symbol
• maximum 24 nodes
• suggest directed flow
• clean geometric shapes
• avoid literal graph diagrams
• reflection implied via symmetry or mirroring
Avoid:
• neural networks
• brain imagery
• infinity loops
• cloud / SaaS icons
• gradients
• complex network diagrams
• mascots
Color palette
Dark primary version:
background: deep slate / near-black (#0D1117 range)
Accent color (choose one):
• deep teal
• indigo-violet
• amber
Maximum two colors + background.
Must also work in monochrome.
Typography
Wordmark: Ontoref
Style:
• geometric monospace or semi-monospace
• technical, calm, restrained
• similar spirit to developer tools and programming languages
Optional tagline under the wordmark.
Deliver
• symbol + wordmark
• symbol-only version
• dark background version
• light background version
• clean vector style
Generate three distinct logo directions:
1. Mirror Node — a node reflecting itself through symmetry or negative space
2. Acyclic Flow — minimal DAG structure with a directional edge suggesting motion
3. Reflective Structure — geometric symbol where half implies structure and half implies reflection
Flat vector logo, precise geometry, minimal tech aesthetic.
vector logo, flat design, minimal geometry, centered symbol, white space, professional software project identity

23
assets/presentation/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
node_modules/
dist/
.DS_Store
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.env
.env.local
.env.*.local
pnpm-lock.yaml
yarn.lock
package-lock.json
.vscode/
.idea/
*.swp
*.swo
*~
.cache/
.nuxt/
build/
.output/
coverage/

View File

@ -0,0 +1,11 @@
import { resolve } from 'node:path'
import { parser } from '@slidev/cli'
const data = await parser.load(resolve('.'), resolve('slides.md'))
console.log('slides:', data.slides.length)
for (let i = 0; i < data.slides.length; i++) {
const s = data.slides[i]
const first = (s.content || '').split(/\r?\n/).find(l => l.trim() && !l.trim().startsWith('<!--')) || '[no text]'
const name = s.frontmatter?.name ? ` name=${s.frontmatter.name}` : ''
console.log(`${String(i + 1).padStart(2, '0')}\t${first.slice(0, 80)}${name}`)
}

View File

@ -0,0 +1,94 @@
import fs from 'node:fs'
const timed = fs.readFileSync('reader-script-en-live-30min.md', 'utf8').split(/\r?\n/)
const live = fs.readFileSync('reader-script-en-live.md', 'utf8').split(/\r?\n/)
const slideTimes = new Map()
for (const line of timed) {
const m = line.match(/^## Slide\s+(\d+)(?:.*)?\[target:\s*(\d+)s\s*\|\s*cumulative:\s*([0-9:]+)\]/)
if (m) slideTimes.set(Number(m[1]), { target: Number(m[2]), cumulative: m[3] })
}
const slideLines = new Map()
let cur = null
for (const line of live) {
const h = line.match(/^## Slide\s+(\d+)/)
if (h) { cur = Number(h[1]); slideLines.set(cur, []); continue }
if (!cur) continue
if (!line.trim()) continue
if (/^(STORY|TENSION|EMPHASIS|DELIVERY):/.test(line)) continue
if (/^\(No spoken text/.test(line)) continue
slideLines.get(cur).push(line.trim())
}
const blocks = [
{ id: 'B1', name: 'Hook & Promise', range: [1,3], goal: 'Abrir con credibilidad y dolor real.' },
{ id: 'B2', name: 'Escalation', range: [4,7], goal: 'Mostrar que la complejidad crece sin control.' },
{ id: 'B3', name: 'Problem Anatomy', range: [8,14], goal: 'Hacer evidente la causa raiz y el costo de fallar tarde.' },
{ id: 'B4', name: 'Turning Point', range: [15,18], goal: 'Giro narrativo: no faltan tools, falta paradigma.' },
{ id: 'B5', name: 'Resolution by Types', range: [19,23], goal: 'Conectar Rust/tipos con respuestas concretas.' },
{ id: 'B6', name: 'Proof in Production', range: [24,31], goal: 'Probar con casos reales e impacto operativo.' },
{ id: 'B7', name: 'Close & CTA', range: [32,35], goal: 'Cerrar con mensaje memorable y accion.' },
]
function mmss(sec) {
const m = Math.floor(sec / 60)
const s = sec % 60
return `${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}`
}
let out = '# Rustikon 2026 - Frases Criticas por Bloques (30 min)\n\n'
out += 'Guia de control narrativo para saber si vas en tiempo o debes acelerar.\n\n'
out += '## Resumen de Bloques\n\n'
out += '| Bloque | Slides | Objetivo narrativo | Tiempo bloque | Acumulado |\n'
out += '|---|---:|---|---:|---:|\n'
let cum = 0
for (const b of blocks) {
let sec = 0
for (let n=b.range[0]; n<=b.range[1]; n++) sec += (slideTimes.get(n)?.target ?? 0)
cum += sec
out += `| ${b.id} ${b.name} | ${b.range[0]}-${b.range[1]} | ${b.goal} | ${mmss(sec)} | ${mmss(cum)} |\n`
}
out += '\n## Señales de Ritmo\n\n'
out += '- Si en **10:00** no has llegado a Slide 12-13, acelera (reduce ejemplos y tablas).\n'
out += '- Si en **20:00** no has llegado a Slide 24, recorta explicacion tecnica y ve a casos reales.\n'
out += '- En **27:00** deberias estar entrando al cierre (Slide 32+).\n\n'
for (const b of blocks) {
out += `## ${b.id} - ${b.name} (Slides ${b.range[0]}-${b.range[1]})\n\n`
let blockSec = 0
for (let n=b.range[0]; n<=b.range[1]; n++) blockSec += (slideTimes.get(n)?.target ?? 0)
out += `Tiempo objetivo bloque: **${mmss(blockSec)}**\n\n`
out += `Objetivo: ${b.goal}\n\n`
out += 'Frases criticas por slide:\n'
for (let n=b.range[0]; n<=b.range[1]; n++) {
const lines = (slideLines.get(n) || []).filter(l => /[A-Za-z]/.test(l))
const critical = lines.slice(0, 2)
if (!critical.length) {
out += `- Slide ${n}: (sin texto hablado, avanzar)\n`
continue
}
out += `- Slide ${n}: ${critical.join(' / ')}\n`
}
out += '\nRegla de enfasis:\n'
if (b.id === 'B1') out += '- Habla lento. No vendas tecnologia; vende problema vivido.\n\n'
else if (b.id === 'B2') out += '- Sube energia y marca cada "lesson" con pausa corta.\n\n'
else if (b.id === 'B3') out += '- Maxima claridad. Una idea por frase.\n\n'
else if (b.id === 'B4') out += '- Haz silencio antes y despues del giro.\n\n'
else if (b.id === 'B5') out += '- Tecnico, pero aterrizado a impacto operativo.\n\n'
else if (b.id === 'B6') out += '- Menos teoria, mas evidencia: MTTR, DR, 3AM.\n\n'
else out += '- Baja ritmo y cierra con conviccion.\n\n'
}
out += '## Checkpoints Rápidos\n\n'
out += '- 05:00 -> deberias estar en Slide 6\n'
out += '- 10:00 -> deberias estar en Slide 12-13\n'
out += '- 15:00 -> deberias estar en Slide 18-19\n'
out += '- 20:00 -> deberias estar en Slide 24\n'
out += '- 25:00 -> deberias estar en Slide 29-30\n'
out += '- 30:00 -> Slide 35 (end)\n'
fs.writeFileSync('critical-phrases-blocks-30min.md', out)
console.log('Wrote critical-phrases-blocks-30min.md')

View File

@ -0,0 +1,199 @@
import fs from 'node:fs'
import { resolve } from 'node:path'
import { parser } from '@slidev/cli'
const TOTAL_SECONDS = 30 * 60
const data = await parser.load(resolve('.'), resolve('slides.md'))
const slides = data.slides.map((s, i) => ({
n: i + 1,
name: s.frontmatter?.name || null,
raw: s.content || '',
}))
function stripText(raw) {
let t = raw
t = t.replace(/<!--[\s\S]*?-->/g, ' ')
t = t.replace(/<style[\s\S]*?<\/style>/gi, ' ')
t = t.replace(/```[\s\S]*?```/g, ' [code example] ')
t = t.replace(/<[^>]+>/g, ' ')
t = t.replace(/^\s*::.*::\s*$/gm, ' ')
t = t.replace(/^\s*#\s*/gm, '')
t = t.replace(/^\s*[-*]\s+/gm, '')
t = t.replace(/^\s*\d+\.\s+/gm, '')
t = t.replace(/\[(.*?)\]\((.*?)\)/g, '$1')
t = t.replace(/[`*_~]/g, '')
t = t.replace(/\|/g, ' ')
t = t.replace(/\n{3,}/g, '\n\n')
const lines = t.split(/\r?\n/).map(l => l.trim()).filter(Boolean)
return lines
}
function simplify(line) {
let t = line
const repl = [
[/\binfrastructure\b/gi, 'infra'],
[/\bconfiguration\b/gi, 'config'],
[/\bvalidation\b/gi, 'check'],
[/\breliability\b/gi, 'stable work'],
[/\bdeclarative\b/gi, 'declared'],
[/\borchestrator\b/gi, 'controller'],
[/\bdistributed\b/gi, 'spread out'],
[/\bparadigm\b/gi, 'approach'],
[/\bpragmatism\b/gi, 'practical work'],
[/\bguarantees\b/gi, 'safety rules'],
[/\bprovisioning\b/gi, 'setup'],
[/\bcompiler\b/gi, 'compiler check'],
]
for (const [re, to] of repl) t = t.replace(re, to)
t = t.replace(/\bIaC\b/g, 'I A C').replace(/\bCI\/CD\b/g, 'C I slash C D').replace(/24×7×365/g, 'all day, every day')
t = t.replace(/\s+—\s+/g, '. ').replace(/\s+->\s+/g, '. then ').replace(/,\s*/g, '. ')
t = t.replace(/\s{2,}/g, ' ').trim()
return t
}
function say(line) {
const rules = [
[/\bInfrastructure\b/gi, 'Infrastructure (IN-fruh-STRUHK-cher)'],
[/\bautomation\b/gi, 'automation (aw-tuh-MAY-shun)'],
[/\bconfiguration\b/gi, 'configuration (kun-fig-yuh-RAY-shun)'],
[/\bprovisioning\b/gi, 'provisioning (pruh-VIH-zhuh-ning)'],
[/\bcompiler\b/gi, 'compiler (kum-PIE-ler)'],
[/\breliability\b/gi, 'reliability (rih-lai-uh-BIH-luh-tee)'],
[/\bvalidation\b/gi, 'validation (val-ih-DAY-shun)'],
[/\bdeclarative\b/gi, 'declarative (dih-KLAR-uh-tiv)'],
[/\borchestrator\b/gi, 'orchestrator (OR-kes-tray-ter)'],
[/\bparadigm\b/gi, 'paradigm (PAIR-uh-dym)'],
[/\bRust\b/g, 'Rust (rahst)'],
[/\bSerde\b/g, 'Serde (SER-dee)'],
[/\bOption\b/g, 'Option (OP-shun)'],
[/\benum\b/gi, 'enum (EE-num)'],
[/\bKubernetes\b/gi, 'Kubernetes (koo-ber-NET-eez)'],
[/\bAnsible\b/gi, 'Ansible (AN-suh-buhl)'],
[/\bTerraform\b/gi, 'Terraform (TEH-ruh-form)'],
[/\bCI\/CD\b/g, 'CI/CD (see-eye / see-dee)'],
[/\bIaC\b/g, 'IaC (eye-ay-see)'],
]
let t = line
for (const [re, rep] of rules) t = t.replace(re, rep)
t = t.replace(/\s+—\s+/g, ' / ').replace(/\.\s+/g, '. / ').replace(/:\s+/g, ': / ')
return t
}
function phase(n) {
if (n <= 3) return ['Act 1 - Hook & Promise', 'Low -> Medium', 'Credibilidad y dolor real', 'Calma, pausas limpias']
if (n <= 7) return ['Act 2 - Escalation', 'Medium -> High', 'Crecimiento de complejidad sin control', 'Ritmo creciente']
if (n <= 14) return ['Act 3 - Problem Anatomy', 'High', 'Causa raiz y costo de fallar tarde', 'Didactico, frases cortas']
if (n <= 18) return ['Act 4 - Turning Point', 'Peak', 'No faltan tools, falta paradigma', 'Silencios intencionales']
if (n <= 23) return ['Act 5 - Resolution', 'High -> Medium', 'Types y compiler como respuesta', 'Claro y tecnico']
if (n <= 31) return ['Act 6 - Proof in Production', 'Medium -> High', 'Casos reales e impacto operativo', 'Energetico y concreto']
return ['Act 7 - Close & CTA', 'Medium -> Low', 'Cierre emocional con accion', 'Lento y memorable']
}
const slideTexts = slides.map(s => ({ ...s, lines: stripText(s.raw) }))
// 1) reader-script-en.md
let out = '# Rustikon 2026 - Slide Text (Reader Version)\n\n'
out += 'One section per real Slidev slide (parser-based count).\n\n'
for (const s of slideTexts) {
out += `## Slide ${s.n}${s.name ? ` (name: ${s.name})` : ''}\n\n`
const [story, tension, emphasis, delivery] = phase(s.n)
out += `STORY: ${story}\nTENSION: ${tension}\nEMPHASIS: ${emphasis}\nDELIVERY: ${delivery}\n\n`
if (!s.lines.length) out += '[No readable text]\n\n'
else out += s.lines.join('\n') + '\n\n'
}
fs.writeFileSync('reader-script-en.md', out)
// 2) pronunciation
out = '# Rustikon 2026 - Reader Script with Pronunciation Guide\n\n'
out += 'Use this for practice. Read the SAY line.\n\n'
for (const s of slideTexts) {
out += `## Slide ${s.n}${s.name ? ` (name: ${s.name})` : ''}\n\n`
const [story, tension, emphasis, delivery] = phase(s.n)
out += `STORY: ${story}\nTENSION: ${tension}\nEMPHASIS: ${emphasis}\nDELIVERY: ${delivery}\n\n`
if (!s.lines.length) {
out += 'EN: [No readable text]\nSAY: [No readable text]\n\n'
continue
}
for (const l of s.lines) out += `EN: ${l}\nSAY: ${say(l)}\n\n`
}
fs.writeFileSync('reader-script-en-pronunciation.md', out)
// 3) simple
out = '# Rustikon 2026 - Simple Reader Script (Easy English)\n\n'
out += 'Short and clear lines for easy pronunciation.\n\n'
for (const s of slideTexts) {
out += `## Slide ${s.n}${s.name ? ` (name: ${s.name})` : ''}\n\n`
const [story, tension, emphasis, delivery] = phase(s.n)
out += `STORY: ${story}\nTENSION: ${tension}\nEMPHASIS: ${emphasis}\nDELIVERY: ${delivery}\n\n`
if (!s.lines.length) {
out += '[No readable text]\n\n'
continue
}
for (const l of s.lines) out += `${simplify(l)}\n\n`
}
fs.writeFileSync('reader-script-en-simple.md', out)
// 4) live
out = '# Rustikon 2026 - Live Reader Script (Speech Only)\n\n'
out += 'Only lines to speak. Legend: `/` short pause, `//` long pause.\n\n'
for (const s of slideTexts) {
out += `## Slide ${s.n}${s.name ? ` (name: ${s.name})` : ''}\n\n`
const [story, tension, emphasis, delivery] = phase(s.n)
out += `STORY: ${story}\nTENSION: ${tension}\nEMPHASIS: ${emphasis}\nDELIVERY: ${delivery}\n\n`
const spoken = s.lines.map(simplify).filter(l => /[A-Za-z]/.test(l) && !/^[-=]+$/.test(l))
if (!spoken.length) {
out += '(No spoken text. Move forward.)\n\n'
continue
}
for (const l of spoken) out += `${l.replace(/\.\s+/g, '. / ').replace(/\s+—\s+/g, ' / ')}\n\n`
}
fs.writeFileSync('reader-script-en-live.md', out)
// 5) live 30-min
const weights = slideTexts.map(s => {
const words = s.lines.join(' ').split(/\s+/).filter(Boolean).length
return 6 + words
})
let secs = weights.map(w => Math.round((w / weights.reduce((a,b)=>a+b,0)) * TOTAL_SECONDS))
const minSec=15,maxSec=90
secs = secs.map(v => Math.max(minSec, Math.min(maxSec, v)))
let diff = TOTAL_SECONDS - secs.reduce((a,b)=>a+b,0)
while (diff !== 0) {
let changed = false
for (let i=0; i<secs.length && diff!==0; i++) {
if (diff > 0 && secs[i] < maxSec) { secs[i]++; diff--; changed = true }
else if (diff < 0 && secs[i] > minSec) { secs[i]--; diff++; changed = true }
}
if (!changed) break
}
const mmss = (t)=>`${String(Math.floor(t/60)).padStart(2,'0')}:${String(t%60).padStart(2,'0')}`
out = '# Rustikon 2026 - Live Reader (30-Min Timing)\n\n'
out += 'Total target: 30:00 (1800s)\n\n'
out += 'How to use:\n- Keep your pace near each slide target.\n- If you are late, cut one example line and move on.\n- If you are early, add one short emphasis line.\n\n'
let cum = 0
for (let i=0;i<slideTexts.length;i++) {
const s = slideTexts[i]
cum += secs[i]
out += `## Slide ${s.n}${s.name ? ` (name: ${s.name})` : ''} [target: ${secs[i]}s | cumulative: ${mmss(cum)}]\n\n`
const [story, tension, emphasis, delivery] = phase(s.n)
out += `STORY: ${story}\nTENSION: ${tension}\nEMPHASIS: ${emphasis}\nDELIVERY: ${delivery}\n\n`
const spoken = s.lines.map(simplify).filter(l => /[A-Za-z]/.test(l) && !/^[-=]+$/.test(l))
if (!spoken.length) out += '(No spoken text. Move forward.)\n\n'
else for (const l of spoken) out += `${l.replace(/\.\s+/g, '. / ')}\n\n`
}
out += '## Checkpoints\n\n'
for (const mark of [5,10,15,20,25,30]) {
const target = mark * 60
let c = 0, slideN = 1
for (let i=0;i<secs.length;i++) { c += secs[i]; if (c >= target) { slideN = i + 1; break } }
out += `- ${String(mark).padStart(2,'0')}:00 -> around Slide ${slideN}\n`
}
fs.writeFileSync('reader-script-en-live-30min.md', out)
// 6) key moments for 35 slides
const key = `# Rustikon 2026 - Key Story Moments\n\nUse this as your minimal narrative map for a 30-minute talk.\n\n## Moment 1 - Hook (Slides 1-3)\nPurpose: Win attention and establish credibility.\n\nAnchor line:\n"I have lived this problem for decades, and I wanted one thing: to sleep."\n\n## Moment 2 - Rising Tension (Slides 4-7)\nPurpose: Show complexity growing faster than control.\n\nAnchor line:\n"We changed tools many times, but not the model."\n\n## Moment 3 - Crisis / Root Cause (Slides 8-14)\nPurpose: Make the cost of late failure undeniable.\n\nAnchor line:\n"Fail late is expensive. Fail early is cheap."\n\n## Moment 4 - Turning Point (Slides 15-18)\nPurpose: Reframe the problem.\n\nAnchor line:\n"The problem was not the tools. The problem was the paradigm."\n\n## Moment 5 - Proof in Production (Slides 24-31)\nPurpose: Show real operational evidence.\n\nAnchor line:\n"At 3 AM, the system responds first. Not me."\n\n## Moment 6 - Emotional Close + CTA (Slides 33-35)\nPurpose: Close with trust and clear action.\n\nAnchor line:\n"Rust gave me deterministic systems and better sleep."\n\n## One-Line Backup Version\n1. I lived this problem for decades.\n2. Complexity grew; certainty did not.\n3. Late failures are the real tax.\n4. Types changed the paradigm.\n5. Production proof: lower MTTR and deterministic workflows.\n6. Start small: model infrastructure as types.\n`
fs.writeFileSync('key-moments-storytelling.md', key)
console.log('Real Slidev slides:', slideTexts.length)
console.log('Docs regenerated with parser-based slide count.')

View File

@ -0,0 +1,215 @@
# Talk Structure: Why I Needed Rust
> Orientado a: `abstract_en.md`
> Fuente: `2026-02-17-notas_voz.md`
---
## Sistema de Medidores *(elemento visual persistente)*
Tres barras que aparecen en esquina de cada slide de transición.
Escala 05. Se actualizan en cada etapa clave.
```
🛡 Confianza ●●●●● = máxima certeza sobre el sistema
😴 Descanso ●●●●● = puedes dormir tranquilo
🔥 Estrés ●●●●● = alarma permanente, pesadillas
```
**Estado inicial (apertura)**
```
🛡 ●●●○○ 😴 ●●●○○ 🔥 ●○○○○
```
*Se presenta como "¿Reconoces estos números? Sigamos..."*
**Progresión a lo largo de la charla:**
| Momento | 🛡 Confianza | 😴 Descanso | 🔥 Estrés |
|---|:---:|:---:|:---:|
| Etapa 1 — Local | ●●●●○ | ●●●●○ | ●○○○○ |
| Etapa 2 — Redes | ●●●○○ | ●●●○○ | ●●○○○ |
| Etapa 3 — Cloud/CI-CD | ●●○○○ | ●○○○○ | ●●●●○ |
| YAML hell / peak pain | ●○○○○ | ○○○○○ | ●●●●● |
| Rust — compilador | ●●●○○ | ●●○○○ | ●●●○○ |
| Rust — tipos + traits | ●●●●○ | ●●●●○ | ●●○○○ |
| Cierre | ●●●●● | ●●●●● | ●○○○○ |
> El título de la charla ES el último estado del medidor de sueño.
---
## 0. Hook de apertura
**Imagen concreta**: definir un host, un nodo, un puerto.
- Eso es todo. Queremos que algo corra en algún sitio y que algo pueda hablar con él.
- ¿Cómo de difícil puede ser?
**Promesa**: vamos a ver por qué eso, durante 30 años, nos ha dado pesadillas. Y por qué Rust cambió eso.
*→ Mostrar medidores en interrogante o en estado inicial neutro*
---
## 1. Evolución de los desafíos en infraestructura (2013-2025)
*Abstract: "The evolution of infrastructure challenges (2013-2025)"*
### Etapa 1 — Local (finales 80s / primeros 90s)
- Terminales tontos, desarrollo local
- Ciclos de despliegue largos, baja urgencia
- Un solo estado, fácil de observar y controlar
- IaC: scripts procedurales, lógica oculta en comandos o en la aplicación
> *Medidores fin Etapa 1:* `🛡 ●●●●○ 😴 ●●●●○ 🔥 ●○○○○`
> *"Tienes un servidor. Sabes lo que tiene. Puedes dormir."*
### Etapa 2 — Conectividad en red / Internet
- Los sistemas están cada vez más lejos — acceso remoto, redes distantes
- Seguridad empieza a importar; procesos críticos, coste de caída sube
- Más agentes participando (dev, ops, seguridad) — hay que armonizar
- Armonizar: instalación de paquetes, configuración de recursos, actualizaciones en paralelo
- IaC: procesos automatizables y reproducibles, primeros intentos declarativos, config-driven model
> *Medidores fin Etapa 2:* `🛡 ●●●○○ 😴 ●●●○○ 🔥 ●●○○○`
> *"Más piezas. Más personas. Empieza a ponerse interesante."*
### Etapa 3 — Contenedores / Cloud / CI-CD
- Monolítico → distribuido, 24×7×365, alta disponibilidad
- Cloud, híbrido, multi-cloud, on-prem — todo a la vez
- Rollback y rollforward: como transacciones de BD, pero en infraestructura
- Escalado horizontal **y** vertical, también **desescalado** — en ambas direcciones
- CI/CD continuo, ciclos cortos — nuevas funcionalidades, nuevos despliegues, permanente
- IaC: Helm, Ansible, Terraform — más herramientas, más complejidad
- Múltiples versiones de "lo que creemos que es válido" — la fuente de verdad se fragmenta
- **Resultado**: estados de alarma como norma, no como excepción
> *Medidores fin Etapa 3:* `🛡 ●●○○○ 😴 ●○○○○ 🔥 ●●●●○`
> *"¿Hemos aumentado la productividad? Sí.<br> ¿Hemos aumentado el estrés? Sí.<br> ¿Hemos aumentado las posibilidades de tener problemas? También.<br> ¿Tenemos más control y seguridad? No."*
---
## 2. Por qué el IaC tradicional falla a escala
*Abstract: "Why traditional approaches to IaC fall short at scale"*
### El ciclo del YAML hell
1. JSON entre máquinas funciona bien → pero es ilegible para humanos
2. Pasamos a YAML / TOML → sintaxis frágil, errores de indentación
3. Usamos gestores de plantillas (Helm, Jinja) → generan YAML a partir de variables en YAML
4. **¿Valida el contenido?** No. En absoluto.
5. Cruzamos los dedos.
> *Medidores — YAML hell peak:* `🛡 ●○○○○ 😴 ○○○○○ 🔥 ●●●●●`
> *"Esto es el suelo. A partir de aquí, o seguimos igual o buscamos algo mejor."*
### Las tres preguntas sin respuesta
**Pregunta 1: ¿Por qué esperamos a que se rompa?**
- "In my machine it works" — en producción, no lo sé
- Fail late = coste máximo
- Queremos: fail fast, fail cheap
**Pregunta 2: ¿Tenemos claro lo que queremos?**
- ¿La declaración es suficiente? ¿Consistente con lo que es posible?
- ¿Respeta los límites y reglas del sistema?
- ¿Cuál es la "fuente de verdad" y cuándo muta?
**Pregunta 3: ¿Tenemos herramientas que garanticen determinismo?**
- CI/CD continuo sin validación semántica = esperanza continua
- Queremos certeza, no aleatoriedad
- "En mi máquina funciona" no puede ser el estándar de producción
### Analogía del restaurante *(hilo conductor)*
| Restaurante | Infraestructura |
|---|---|
| Comensal declara lo que quiere | Config declarativa (YAML, HCL) |
| Camarero valida si es posible | Orchestrator (K8s, Ansible) |
| Cocina ejecuta y entrega | Runtime / provisioning |
| El plato llega o no llega | Deployment falla o no |
- El camarero no puede volver a cocina con un pedido imposible
- La cocina no puede asumir ingredientes que no están
- Cada actor en la cadena tiene una vista parcial de "la verdad"
- Un fallo en mesa ≠ un fallo en cocina ≠ un fallo en entrega → coste diferente en cada punto
---
## 3. Type safety y memory safety como fiabilidad en producción
*Abstract: "Type safety and memory safety as production reliability"*
### Lo que Rust introduce
**Tipado estático estricto**
- Un número no es un string. Un puerto no es cualquier entero.
- Tipos primarios ricos + tipos compuestos (structs, enums)
- Las conversiones (cast) son explícitas, no implícitas
**Inmutabilidad por defecto**
- "Quiero tomate. Puedes inventarte lo que quieras, pero sin tomate no es el plato que pedí."
- Lo que no puede cambiar, no cambia. Sin sorpresas en runtime.
**Options, no nulos**
- No `null`, no "asume que está". `Option<T>`: está o no está, y ambos casos son manejados.
- Ingrediente opcional: si no está, lo sé. Lo cobro o no. Pero lo controlo.
**Enums y rangos como restricciones**
- Un puerto válido es un rango entre 0 y 65535, no cualquier número
- Un cloud provider es `AWS | GCP | Azure | OnPrem`, no un string libre
- Las restricciones son parte del tipo, no de la documentación
**Traits como contratos**
- Composición en vez de herencia
- Implementaciones validadas contra interfaces definidas
- Diferentes "actores" en la cadena pueden tener diferentes implementaciones del mismo contrato
**El compilador como pre-validación**
- Valida *antes* de construir el binario
- No cuando lleva tiempo en ejecución
- No cuando se llama a una función que hace meses que nadie toca
- Binarios predecibles en comportamiento de memoria, recursos y flujos
> *"El compilador es el camarero que valida el pedido antes de que llegue a cocina."*
> *Medidores — Rust completo:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
---
## 4. Orquestación segura multi-cloud y on-prem
*Abstract: "Building safe orchestration across multi-cloud and on-prem environments"*
*(Sección a desarrollar — no cubierta en las notas de voz actuales)*
Puntos a cubrir:
- Tipos como esquema de infraestructura → no YAML libre
- Traits para abstraer providers (AWS, GCP, on-prem, K8s)
- Compilador detecta configuraciones imposibles antes del despliegue
- Estado mutable explícito → sin configuration drift silencioso
---
## 5. Aplicaciones reales
*Abstract: "Real applications: Kubernetes, blockchain validators, disaster recovery"*
*(Sección a desarrollar — no cubierta en las notas de voz actuales)*
Casos concretos:
- Kubernetes: operadores en Rust, CRDs con tipos seguros
- Blockchain validators: disponibilidad crítica, configuración determinista
- Disaster recovery: reproducibilidad, sin "funciona en prod pero no en DR"
---
## Cierre
- No hemos inventado nada nuevo. Los problemas siempre han existido.
- Rust los reúne y los resuelve: tipos, traits, compiler, memory safety.
- El resultado: infraestructura predecible. Deployments sin pesadillas.
- **Duermes bien.**
> *Medidores — cierre:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
>
> *Slide final: los tres medidores en verde. Título de la charla. Fin.*

View File

@ -0,0 +1,534 @@
# Talk Structure: Why I Needed Rust
> Orientado a: `abstract_en.md`
> Fuente: `2026-02-17-notas_voz.md`
---
## Sistema de Medidores *(elemento visual persistente)*
Tres barras que aparecen en esquina de cada slide de transición.
Escala 05. Se actualizan en cada etapa clave.
```
🛡 Confianza ●●●●● = máxima certeza sobre el sistema
😴 Descanso ●●●●● = puedes dormir tranquilo
🔥 Estrés ●●●●● = alarma permanente, pesadillas
```
**Estado inicial (apertura)**
```
🛡 ●●●○○ 😴 ●●●○○ 🔥 ●○○○○
```
*Se presenta como "¿Reconoces estos números? Sigamos..."*
**Progresión a lo largo de la charla:**
| Momento | 🛡 Confianza | 😴 Descanso | 🔥 Estrés |
|---|:---:|:---:|:---:|
| Etapa 1 — Local | ●●●●○ | ●●●●○ | ●○○○○ |
| Etapa 2 — Redes | ●●●○○ | ●●●○○ | ●●○○○ |
| Etapa 3 — Cloud/CI-CD | ●●○○○ | ●○○○○ | ●●●●○ |
| YAML hell / peak pain | ●○○○○ | ○○○○○ | ●●●●● |
| Rust — compilador | ●●●○○ | ●●○○○ | ●●●○○ |
| Rust — tipos + traits | ●●●●○ | ●●●●○ | ●●○○○ |
| Cierre | ●●●●● | ●●●●● | ●○○○○ |
> El título de la charla ES el último estado del medidor de sueño.
---
## 0. Hook de apertura
**Imagen concreta**: definir un host, un nodo, un puerto.
- Eso es todo. Queremos que algo corra en algún sitio y que algo pueda hablar con él.
- ¿Cómo de difícil puede ser?
**Promesa**: vamos a ver por qué eso, durante 30 años, nos ha dado pesadillas. Y por qué Rust cambió eso.
*→ Mostrar medidores en interrogante o en estado inicial neutro*
---
## 1. Evolución de los desafíos en infraestructura (2013-2025)
*Abstract: "The evolution of infrastructure challenges (2013-2025)"*
### Etapa 1 — Local (finales 80s / primeros 90s)
- Terminales tontos, desarrollo local
- Ciclos de despliegue largos, baja urgencia
- Un solo estado, fácil de observar y controlar
- IaC: scripts procedurales, lógica oculta en comandos o en la aplicación
> *Medidores fin Etapa 1:* `🛡 ●●●●○ 😴 ●●●●○ 🔥 ●○○○○`
> *"Tienes un servidor. Sabes lo que tiene. Puedes dormir."*
### Etapa 2 — Conectividad en red / Internet
- Los sistemas están cada vez más lejos — acceso remoto, redes distantes
- Seguridad empieza a importar; procesos críticos, coste de caída sube
- Más agentes participando (dev, ops, seguridad) — hay que armonizar
- Armonizar: instalación de paquetes, configuración de recursos, actualizaciones en paralelo
- IaC: procesos automatizables y reproducibles, primeros intentos declarativos, config-driven model
> *Medidores fin Etapa 2:* `🛡 ●●●○○ 😴 ●●●○○ 🔥 ●●○○○`
> *"Más piezas. Más personas. Empieza a ponerse interesante."*
### Etapa 3 — Contenedores / Cloud / CI-CD
- Monolítico → distribuido, 24×7×365, alta disponibilidad
- Cloud, híbrido, multi-cloud, on-prem — todo a la vez
- Rollback y rollforward: como transacciones de BD, pero en infraestructura
- Escalado horizontal **y** vertical, también **desescalado** — en ambas direcciones
- CI/CD continuo, ciclos cortos — nuevas funcionalidades, nuevos despliegues, permanente
- IaC: Helm, Ansible, Terraform — más herramientas, más complejidad
- Múltiples versiones de "lo que creemos que es válido" — la fuente de verdad se fragmenta
- **Resultado**: estados de alarma como norma, no como excepción
> *Medidores fin Etapa 3:* `🛡 ●●○○○ 😴 ●○○○○ 🔥 ●●●●○`
> *"¿Hemos aumentado la productividad? Sí.<br> ¿Hemos aumentado el estrés? Sí.<br> ¿Hemos aumentado las posibilidades de tener problemas? También.<br> ¿Tenemos más control y seguridad? No."*
---
## 2. Por qué el IaC tradicional falla a escala
*Abstract: "Why traditional approaches to IaC fall short at scale"*
### La analogía del restaurante *(hilo conductor de toda la charla)*
Un restaurante tiene tres actores: **comensal** (mesa), **camarero** (servicio), **cocina**.
| Restaurante | Infraestructura |
|---|---|
| Comensal declara lo que quiere | Config declarativa (YAML, HCL, Helm) |
| Camarero valida y transmite | Orchestrator (K8s, Ansible, Terraform) |
| Cocina ejecuta y entrega | Runtime / provisioning |
| El plato llega — o no | Deployment exitoso — o no |
**Lo que hace que funcione** — o no:
- El comensal declara, no implementa: no dice *cómo* cocinar, dice *qué* quiere
- El camarero debe saber qué es posible y validar *antes* de ir a cocina
- "Quiero X" → camarero va a cocina → "X no hay, ¿por qué está en la carta?" → vuelve a mesa
- Equivalente: configuré un host con el puerto 8443 → ese puerto no está permitido → hay que reconfigurar desde cero
**La verdad que muta — y el problema de estado**
- La "verdad" no es estática. Muta en cada paso de la cadena:
- Comensal: su petición oral
- Camarero: la nota que toma
- Cocina: las marcas sobre lo que ya está hecho o no
- Caja: el ticket de pago
- Cada actor ve una *parte* de la verdad, relevante para él en ese momento
- Como en un sistema neuronal: hay eventos que no activan ciertas neuronas porque no son relevantes para ellas
- **El camarero conoce al cliente habitual** ("siempre sin sal") — la cocina, no. ¿Está ese contexto explícito en el pedido, o asumido?
- Si el camarero cambia, o si interviene otra estación de cocina → el estado implícito se pierde
**El coste del fallo depende de dónde ocurre**
- Fallo en mesa al pedir (pedido imposible): barato — se corrige antes de llegar a cocina
- Fallo en cocina al elaborar (ingrediente no disponible): medio — hay que volver a mesa y renegociar
- Fallo al entregar (plato se cae): caro — hay que rehacer desde cero
- Fallo cuando llega el plato y no es lo que se pidió: muy caro — experiencia destruida + tiempo perdido
- **Fail early = fail cheap. Fail in production = nightmare.**
**La renegociación: "no tengo champiñón"**
- Un actor en la cadena descubre que no puede cumplir parte del pedido
- Debe volver atrás y validar con el comensal: "¿te pongo verduras?"
- Esto requiere que el cambio sea explícito, trazado, y re-autorizado — no silencioso
- Equivalente en infra: configuration drift, estado divergente sin notificación
---
### El ciclo de la config: cómo llegamos hasta aquí
1. **Hardcoded** — dependencias, rutas, puertos dentro del binario. Control total, cero flexibilidad
2. **Config externa** (JSON) — funciona bien entre máquinas, ilegible para humanos a escala
3. **YAML / TOML** — más legible, pero sintaxis frágil: indentación, tipos implícitos, errores silenciosos
4. **YAML + Serde** — cargamos la config con Serde, que valida la *estructura*:
- ¿Existe el campo? ¿Es del tipo correcto?
- ¿Aceptamos "elefante" como animal de compañía? Depende del tipo. Pero si el tipo es `String`... sí.
- **Serde valida forma. No valida significado.**
5. **Helm / Jinja templates** — escribimos YAML a partir de variables (en YAML o JSON)
- El template genera el YAML final que consume la aplicación
- ¿Valida el contenido del YAML generado? **No. En absoluto.**
- Como usar un LLM con un markdown de referencia: el formato está, pero ¿el contenido es correcto? Eso no lo garantiza nadie.
6. **Resultado**: CI/CD continuo + config sin validación semántica = **esperanza continua**
> *"Lo que hacemos es escribir lo que queremos, como una carta a los Reyes Magos. Y cruzamos los dedos."*
---
```
┌─────────────────────────────────────────────┐
│ SLIDE STANDALONE — Esperanza continua │
│ │
│ CI/CD continuo. │
│ Sin validación semántica. │
│ │
│ Esperanza continua. │
│ │
│ (cruzamos los dedos en producción) │
└─────────────────────────────────────────────┘
```
> *Medidores:* `🛡 ●○○○○ 😴 ○○○○○ 🔥 ●●●●●`
> *Tono: irónico, de reconocimiento. La audiencia asiente.*
---
### Las tres preguntas sin respuesta
**Pregunta 1 — ¿Por qué esperamos a que se rompa?**
- "In my machine it works" — en producción, no lo sé
- Fail late = coste máximo
- Queremos: fail fast, fail cheap — cuanto antes, menor el coste
**Pregunta 2 — ¿Tenemos claro lo que queremos?**
- ¿La declaración es suficiente y consistente con lo que es posible?
- ¿Respeta los límites del sistema? ¿Cuáles son esos límites — estáticos o dinámicos?
- ¿Cuál es la fuente de verdad? ¿Y cuándo y cómo muta?
**Pregunta 3 — ¿Podemos garantizar determinismo?**
- ¿Tenemos herramientas que usen nuestras definiciones para operar automáticamente en el ciclo de vida?
- ¿Podemos tener certeza de que obtendremos lo que formulamos — no de forma aleatoria?
- No "en mi máquina funciona". Siempre. En cualquier máquina. En cualquier momento.
> *En realidad no estamos inventando nada. Todo ya existe. El problema es si lo estamos gestionando adecuadamente.*
---
```
┌─────────────────────────────────────────────┐
│ SLIDE STANDALONE — Pesadilla continua │
│ │
│ Sistemas que no sabemos cómo controlar. │
│ Esperamos que funcionen. │
│ Cuando no funcionan, los arreglamos. │
│ │
│ Pesadilla continua. │
│ │
│ (el estado de alarma es lo normal) │
└─────────────────────────────────────────────┘
```
> *Medidores:* `🛡 ●○○○○ 😴 ○○○○○ 🔥 ●●●●●`
> *Tono: oscuro, sin ironía. El suelo emocional de la charla. Pausa aquí.*
---
## 3. Type safety y memory safety como fiabilidad en producción
*Abstract: "Type safety and memory safety as production reliability"*
### El puente: de YAML+Serde a tipos Rust
Serde carga config estructuralmente válida. Pero "elefante" como valor de `animal_companía: String` compila.
La respuesta de Rust: **no uses `String`. Usa un tipo.**
```rust
enum Animal { Perro, Gato, Conejo } // "elefante" no compila
```
Eso es lo que cambia. No el formato de la config. El modelo de qué puede contener.
---
### Lo que Rust introduce — respuesta a las tres preguntas
**Tipado estático estricto** *(responde: "¿tenemos claro lo que queremos?")*
- Un número no es un string. Un puerto no es cualquier entero.
- Los tipos son la declaración de intenciones — sin ambigüedad, sin equívocos
- Las conversiones (cast) son explícitas, no implícitas
- Tipos primarios ricos + structs, enums, tuplas, newtypes
**Inmutabilidad por defecto** *(responde: "¿qué puede cambiar?")*
- "Quiero tomate. Puedes inventarte lo que quieras, pero sin tomate no es el plato que pedí."
- `let` es inmutable. `let mut` es explícito y deliberado.
- Los invariantes son invariantes — no silenciosamente mutados en runtime
**`Option<T>`, no nulos** *(responde: "¿qué es opcional y qué es obligatorio?")*
- No `null`, no "asume que está si no se especifica"
- `Option<T>`: o está (`Some`) o no está (`None`) — y ambos casos se manejan explícitamente
- Restaurante: "champiñón opcional. Si no está, lo sé. Lo decido. Lo gestiono."
- El camarero no puede ignorar que no hay champiñón — el compilador no le deja
**Enums como dominio cerrado** *(responde: "¿cuáles son los valores válidos?")*
- Un cloud provider es `enum Provider { AWS, GCP, Azure, OnPrem }` — no un `String` libre
- Un puerto es un `u16` con rango validado — no cualquier entero
- Las restricciones están en el tipo, no en comentarios, no en documentación, no en la memoria del equipo
**Traits como contratos entre actores** *(responde: "¿pueden los actores de la cadena cambiar?")*
- Composición en vez de herencia rígida
- Cada "actor" de la cadena (AWS provider, K8s, on-prem) implementa el mismo trait
- La cadena funciona con cualquier implementación que cumpla el contrato
- Como el restaurante: el camarero puede cambiar — si conoce el protocolo, el proceso es el mismo
**El compilador como el camarero que valida** *(responde: "¿cuándo detectamos el fallo?")*
- Valida **antes** de construir el binario — no en runtime, no en producción
- No cuando lleva horas ejecutándose. No cuando se llama a una función que nadie tocaba hace meses.
- Fail early. Fail cheap. En compilación, no en el 2am del domingo.
- Binarios predecibles: comportamiento de memoria, recursos y flujos deterministas
> *"El compilador es el camarero que valida el pedido antes de que llegue a cocina. Antes de que el cliente espere. Antes de que el ingrediente no esté."*
> *Medidores — compilador activo:* `🛡 ●●●○○ 😴 ●●○○○ 🔥 ●●●○○`
> *Medidores — tipos + traits + options:* `🛡 ●●●●○ 😴 ●●●●○ 🔥 ●●○○○`
> *Medidores — Rust completo en producción:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
---
```
┌─────────────────────────────────────────────┐
│ SLIDE STANDALONE — Certeza continua │
│ │
│ El compilador valida antes del binario. │
│ Los tipos definen lo que es posible. │
│ El contrato se cumple — o no compila. │
│ │
│ Certeza continua. │
│ │
│ (para continuar durmiendo bien) │
└─────────────────────────────────────────────┘
```
> *Medidores:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
> *Tono: resolución. El arco cierra. El título de la charla, demostrado.*
---
## 4. Orquestación segura multi-cloud y on-prem
*Abstract: "Building safe orchestration across multi-cloud and on-prem environments"*
### De la teoría al sistema real
Todo lo anterior no es hipotético. Es la arquitectura de **Provisioning** — un sistema de orquestación de infraestructura en producción, escrito en Rust, que gestiona despliegues en AWS, UpCloud y on-prem desde una sola base de código.
La pregunta es: ¿cómo se traduce "tipos y traits" a un sistema que maneja multi-cloud real?
---
### Nickel como source of truth tipado (ADR-003)
YAML fue rechazado explícitamente. TOML fue rechazado. La razón: sin type safety.
Nickel — un lenguaje de configuración funcional con sistema de tipos — reemplaza YAML:
```nickel
{
infrastructure = {
compute | {
region | String,
count | Number & (> 0),
auto_scaling | {
min | Number & (> 0),
max | Number & (>= min), -- el compilador verifica esto
}
}
}
}
```
- Validación en tiempo de compilación de la config — no en runtime, no en producción
- Merging jerárquico tipado: defaults → workspace → profile → environment → runtime
- Resultado (ADR-003): **zero configuration type errors en producción**
- Alternativas rechazadas explícitamente: TOML (no type safety), KCL, YAML+validación híbrida
> *"Serde valida forma. Nickel valida significado. El compilador valida antes del despliegue."*
---
### Traits como contratos de provider (el restaurante, resuelto)
El problema del restaurante: la cocina puede cambiar. AWS no es UpCloud no es bare metal.
La solución Rust: todos implementan el mismo trait.
```rust
// El trait es el menú. Los providers son las cocinas.
#[async_trait]
pub trait TaskStorage: Send + Sync {
async fn create_task(&self, task: WorkflowTask) -> StorageResult<WorkflowTask>;
async fn update_task(&self, id: &str, status: TaskStatus) -> StorageResult<()>;
async fn list_tasks(&self, filter: TaskFilter) -> StorageResult<Vec<WorkflowTask>>;
async fn audit_operation(&self, op: AuditEntry) -> StorageResult<()>;
}
// Implementaciones: FilesystemStorage, SurrealDbStorage, MemoryStorage
// El orchestrator no sabe cuál usa. El compilador garantiza que todas cumplen.
```
```rust
// Dominios cerrados — no strings libres
enum RollbackStrategy { ConfigDriven, Conservative, Aggressive, Custom { operations: Vec<String> } }
enum DependencyType { Hard, Soft, Optional }
enum TaskStatus { Pending, Running, Completed, Failed, Cancelled }
enum ProviderType { UpCloud, AWS, Local }
```
Cuando se añade un nuevo provider: implementa el trait o no compila. No hay forma de olvidarse de un caso.
---
### Orquestación con grafo de dependencias
El orchestrator construye un DAG (grafo acíclico dirigido) de tareas con detección de ciclos en tiempo de compilación del workflow.
```rust
pub struct WorkflowConfig {
pub max_parallel_tasks: usize,
pub task_timeout_seconds: u64,
pub fail_fast: bool, // falla rápido, falla barato
pub checkpoint_interval_seconds: u64,
}
```
Instalación de Kubernetes como ejemplo — el orchestrator resuelve:
```
containerd (Hard) → etcd (Hard) → kubernetes → cilium (requires kubernetes) → rook-ceph
```
No se puede instalar kubernetes si containerd falla. El tipo `DependencyType::Hard` lo garantiza.
El compilador detecta dependencias circulares. No el operador de guardia a las 3am.
---
### El estado explícito elimina el drift
El problema de configuration drift: el sistema real diverge silenciosamente del estado deseado.
Rust obliga a que toda mutación sea explícita (`let mut`). El sistema de estado del orchestrator hace lo mismo a nivel de infraestructura:
```rust
pub struct WorkflowExecutionState {
pub status: WorkflowStatus,
pub task_states: HashMap<String, TaskExecutionState>,
pub checkpoints: Vec<WorkflowCheckpoint>, // qué se hizo y cuándo
pub statistics: WorkflowStatistics,
}
```
- Checkpoints cada 5 minutos (configurable)
- Hasta 50 checkpoints retenidos
- Cada estado de provider capturado en `HashMap<String, ProviderState>`
- Sin estado implícito. Sin "el camarero recuerda que el cliente no quiere sal". Está en el pedido.
> *Medidores — sección 4:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
---
## 5. Aplicaciones reales
*Abstract: "Real applications: Kubernetes, blockchain validators, disaster recovery"*
### Kubernetes — despliegue con tipos
El orchestrator se despliega en Kubernetes. 3 réplicas, rolling update, anti-affinity entre nodos.
Pero lo relevante no es que *corre* en Kubernetes — es cómo se *gestiona* Kubernetes como target.
El orchestrator provisiona los componentes del cluster como workflow tipado:
```
containerd → etcd → kubernetes control plane
→ kubelet (workers)
→ CoreDNS
→ Cilium (CNI, requires kubernetes)
→ Rook-Ceph (storage, requires kubernetes + Cilium)
```
Cada componente es un tipo. Cada dependencia es un `DependencyType`. El compiler detecta si intentas instalar Cilium sin Kubernetes. No el CI a las 2am.
Los health probes del orchestrator en K8s — `/health` cada 10 segundos — no son decoración. Son parte del contrato: el sistema sabe cuándo está sano. Lo sabe antes de que falle el cliente.
---
### Blockchain validators — disponibilidad como tipo
Los validators de blockchain (Polkadot, Ethereum) tienen un requisito brutal: uptime o slashing. Un validator que falla pierde fondos. No hay margen para "en mi máquina funciona".
Lo que Rust + este sistema aporta:
**Secretos con post-quantum cryptography (ADR-006)**
- CRYSTALS-Kyber (KEM) + Falcon (signatures) + AES-256-GCM (hybrid)
- Las claves del validator no pueden ser comprometidas por computación cuántica futura
- La rotación de claves es automática, trazada, con audit log
**SLOs con error budgets reales (ADR-009)**
```
Tier 1 — Critical: 99.99% uptime = 52.6 minutos de downtime/año
```
Esto no es un número de marketing. Es un Prometheus rule que dispara alertas y bloquea deploys cuando el burn rate supera el presupuesto.
**Configuración determinista**
- Los parámetros del validator son tipos, no strings: no puede entrar un valor de `bond_amount` que no sea un `u128` validado
- La config del validator es reproducible bit-a-bit en cualquier nodo — mismo Nickel schema, mismo resultado
---
### Disaster recovery — rollback como tipo, no como procedimiento
El problema clásico de DR: el runbook existe, pero no se ejecutó correctamente, o el estado en DR no coincide con el de producción.
**Checkpoints como snapshots de estado completo**
```rust
pub struct Checkpoint {
pub workflow_state: Option<WorkflowExecutionState>,
pub resources: Vec<ResourceSnapshot>,
pub provider_states: HashMap<String, ProviderState>,
}
```
No es "vuelve al commit anterior". Es el estado completo del sistema: qué estaba corriendo, en qué provider, con qué configuración, en qué momento.
**Rollback como estrategia tipada**
```rust
enum RollbackStrategy {
ConfigDriven, // lo que diga la config
Conservative, // preserva todo salvo lo marcado
Aggressive, // revierte todo
Custom { operations: Vec<String> }, // playbook explícito
}
```
No hay forma de hacer rollback sin elegir una estrategia. El compilador no te deja ignorar el caso.
**Self-healing automatizado (ADR-010)**
```
┌─────────────────────────────────────────────┐
│ SLIDE STANDALONE — MTTR │
│ │
│ Sin tipos. Sin compilador. Sin estado. │
│ │
│ MTTR > 30 minutos. │
│ │
│ (a las ??, apagar el fuego) Alarma pesadilla│
│ ──────────────────────────────── │
│ │
│ Rust. Tipos. Estado explícito. │
│ Automated response. │
│ │
│ MTTR < 5 minutos.
│ │
│ (a las 3am, sin ti) Descanso en paz mental │
└─────────────────────────────────────────────┘
```
> *Medidores:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
> *Tono: cifras reales, no promesas. Pausa después de "sin ti".*
El `RemediationEngine` ejecuta playbooks tipados: `ScaleService`, `FailoverService`, `RestartService`, `ClearCache`. Si el remedio falla 3 veces, escala a humano. No itera indefinidamente.
**Multi-backend backup tipado**
- restic, borg, tar, rsync — todos como variantes de un enum
- El backup de producción y el restore en DR usan el mismo tipo, el mismo schema
- "Funciona en prod pero no en DR" no puede ocurrir si el estado es el mismo tipo
> *"No estamos cruzando los dedos. Tenemos el estado. Tenemos el tipo. Tenemos el rollback. Dormimos."*
> *Medidores — sección 5:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
---
## Cierre
- No hemos inventado nada nuevo. Los problemas siempre han existido.
- Rust los reúne y los resuelve: tipos, traits, compiler, memory safety.
- El resultado: infraestructura predecible. Deployments sin pesadillas.
- **Duermes bien.**
> *Medidores — cierre:* `🛡 ●●●●● 😴 ●●●●● 🔥 ●○○○○`
>
> *Slide final: los tres medidores en verde. Título de la charla. Fin.*

View File

@ -0,0 +1,101 @@
Why I Needed Rust: Finally, Infrastructure Automation I can sleep on
Description
Esta charla comienza con un problema claro. La definición, por ejemplo, de un host, un nodo, donde se va a ejecutar una aplicación y la forma en la que se va a conectar con ella como un puerto, por ejemplo. Entonces ahora lo que hacemos es un recorrido donde mostramos la evolución de los desafíos en infraestructura en diferentes etapas.
La primera etapa es cuando no había redes y los desarrollos eran en local. Estamos hablando de finales de los 80, primeros de los 90. Había terminales tontos, cómo se desplegaba una aplicación, cuáles eran los ciclos de despliegue, los tiempos, las urgencias y los modos de despliegue. La siguiente etapa es la que aparece en la conectividad en las redes. Con Internet y la web el efecto conectividad de redes se multiplica, la seguridad se hace más relavante y muchos del os procesos son críticos, se incrementan los costes por errores o caídas de servicios.
Los sistemas están cada vez más lejos. Hay que conectarse a través de redes remotas o distantes. Los ciclos, el nivel de computación subido. Hay más participación de diferentes agentes en el desarrollo y en la explotación.
Y hay que armonizar la instalación de paquetes, la configuración de recursos, las actualizaciones y demás. La tercera etapa es con la aparición de los contenedores, pasamos de la computación monolítica a arquitecturas claramente distribuidas, con alta disponibilidad, ya trabajando en entornos 24 x 7, 365 días. Servicios cloud, modos híbridos, sistemas automatizados CD/CI en cíclos contínuos. Infraestructura como código.
Y queremos ser competitivos con unos ciclos muy cortos de nuevas funcionalidades, de nuevos despliegues, con la posibilidad de hacer lo que hacemos con las transacciones en las bases de datos en épocas anteriores, que es volver hacia atrás y volver hacia adelante. Ya no solo escalamos horizontalmente o verticalmente como ocurría en otras épocas, sino en ambas direcciones y también desecalamos.
Y los sistemas se hacen cada vez más complejos. La presión es cada vez mayor. Y las psobilidades de descansar y no tener pesadillas aumenta considerablemente y los estados de alarma terminan siendo lo normal, la capacidad de posibilidades de tener problemas sin que ha aumentado enormemente.
Hemos llegado hasta aquí con lo que conocíamos y teníamos en cada época. Cómo hemos definido las arquitecturas, cómo las hemos construido, planificado, construido y puesto en marcha y cómo hemos trabajado en su mantenimiento cotidiano, en sus actualizaciones y ciclos de cambio. ¿Qué es lo que ocurrido en cada una de estas etapas al respecto?
En la primera etapa apenas con scripts y con detalles más o menos ocultos en scripts o en definiciones de lo que queríamos de modo procedural. Después una etapa donde hay que instalar varios recursos en paralelo, pues aprendemos e intentamos crear procesos automatizables que se puedan reproducir. Pensamos que podemos escribir lo que queremos en una especie de declaración o una configuración que sido validada.
Y a partir de la tercera etapa empezamos a poner más herramientas para controlar esto. Pensamos que más es mejor, como siempre. Más computación, más mano de obra, más trabajo. El enfoque aquí es utilizar ya Mele, quizá en algunos casos Tomele como en el caso de Arras.
Y lo que hacemos es escribir lo que queremos, como una declaración de buenos deseos, como la carta a los reyes o a Papá Noel. Y queremos, cruzamos los dedos con la esperanza de que las cosas se hagan posibles. Monitorizamos, tenemos recursos para hacer seguimiento. Y muchos de los procesos no se repiten hasta que no pasa mucho tiempo en presencia de personas diferentes.
Entonces empezamos a declarar esto, encontrar fórmulas para declarar esto a través del mundo conocido. Y como JSON no es muy legible para humanos, pues terminamos con Yamele, Tomele y este tipo de cosas.
Hemos pasado de un estado único que era fácil de mirar y controlar, a una segunda etapa donde había que tener en cuenta varias cosas, podían ocurrir a la vez o podían producirse de forma más o menos paralela, a procesos en la tercera fase ya con contenedores, con infraestructuras cloud, donde todo el proceso es distribuido, donde es muy fácil tener diferentes versiones de lo que creemos que es válido o lo que sería la fuente de verdad.
Y como esto tiene diferentes perspectivas, diferentes necesidades, con diferentes modos de interpretación, en un mundo de contenedores donde esta manera de distribuir lo que queremos no se hace tan fácil y donde un error tiene resonancia, tiene mucha más repercusión que en las dos primeros etapas.
¿Hemos aumentado nuestra capacidad de hacer cosas, nuestro potencial, la productividad y demás? Sí. ¿Hemos aumentado la complejidad de los recursos? Sí. ¿Hemos creado mucho más estrés a la hora del mantenimiento de estos recursos porque tenemos más dependencia de ellos? Sí. Esto genera mucha fricción y mucha incertidumbre porque hay cada vez más interacciones desde el principio hasta el final.
Sí. ¿Cuál es el resultado de esto? Pues que nos encontramos frente a sistemas que llega a un punto, no sabemos cómo controlar, no tenemos mucha creencia de lo que puede o no funcionar y simplemente esperamos que funcione. Cuando no funciona, entonces lo arreglamos.
Primera pregunta: ¿por qué tenemos que esperar a que no funcione o a que se rompa todo para arreglarlo? ¿Por qué no podemos prevenir antes que curar, fallar lo antes posible con el menor coste posible?
Segunda pregunta, ¿cómo hemos definido lo que queremos? Es decir, independientemente de cómo lo hagamos, ¿tenemos claro lo que queremos? ¿Tenemos claro cómo tiene que ser aquello que tiene que funcionar? ¿En qué términos? ¿Con qué restricciones? ¿Con qué condiciones? ¿Y bajo qué planteamientos? O sea, que hemos encontrado una fórmula para definir cuál es el contexto y los límites o las restricciones a ese contexto en el que queremos que se presten los servicios o bajo qué condiciones.
Tercera pregunta. Hemos encontrado o hemos creado herramientas que utilicen estas definiciones o estos límites o contextos y puedan trabajar de forma automática en los ciclos de vida de las aplicaciones y de los servicios para garantizar que se lleve a cabo lo que queremos, para que se haga en los términos en los que hemos, respetando los términos que hemos acordado previamente, En suma, ¿podemos tener una certeza de que al final tendremos aquello que hemos pedido o que hemos formulado, deseado o declarado? Con la seguridad o la tranquilidad de que eso va a ocurrir de una manera determinista, no de una manera aleatoria, con problemas que pueden ocurrir o no en mi máquina funciona, en aquella ya no lo sé.
La comparación paralela que quiero hacer en cada una de las fases y en cada una de las preguntas y demás es la de cuando se va a un restaurante. Cuando se va al restaurante hay básicamente una cocina, un servicio que atiende al cliente y va y viene de la cocina a la mesa del cliente y un cliente sentado en una mesa, unos clientes sentados en una mesa que son los comensales.
Lo que pasa entre cada una de estas tres partes tiene que ver con la gestión de procesos. Es totalmente declarativa porque el cliente no dice en la mesa cómo en cocina se tiene que elaborar cada uno de los platos que elegido. El cliente simplemente declara lo que quiere, el comensal declara lo que quiere y cuando se va al restaurante le pueden presentar una carta con A, B y C, o una carta en la que A puede tener variantes o sencillamente decirle esto es lo que hay hoy y el cliente elige.
Entonces el servicio que atiende tiene que tener un conocimiento claro de lo que es posible y de lo que no y hacer una validación de esa petición para saber si es posible en tiempo o en forma en función de lo que se pedido. Y por otro lado tiene que entender las mecánicas de la cocina y la elaboración de platos.
Cuando servirlos, cómo servirlos, cómo hacer esa conexión entre los clientes que están en mesa, el mundo del restaurante y el mundo de la cocina. Y con el menor coste, sacrificio y esfuerzo posible porque hay que hacer muchas cosas a la vez.
Analicemos cada una de las cualidades que observamos En cada una de las diferentes etapas que hemos mencionado y en las fases de la petición de platos en un restaurante. En cada una de esas fases y procesos hemos elegido diferentes modos. ¿Qué es lo que tenemos que valorar? Primero, ¿hasta qué punto son eficientes y suficientes para expresar lo que queremos?
No puede ocurrir que yo pida algo para que después el camarero vaya a cocina y el cocinero le diga No, de eso no hay, está fuera temporada, esto hoy no lo tenemos, entonces ¿por qué lo tienes en la carta? etc. No, yo he configurado un host con un puerto pero ese puerto no está permitido, hay que volver y reconfigurar el puerto y volver a empezar de cero.
¿Es esta declaración suficiente y consistente, realista y rigurosa con lo que hay y con lo que es posible de acuerdo con las reglas? ¿Qué reglas? Esas reglas son dinámicas, son permanentes, son para toda la vida, el restaurante sólo ofrece A, B, C y D siempre a todas horas, no hay cambios.
¿Cada cuánto tiempo se producen esos cambios? ¿Cuáles son los ciclos de vida? ¿De cambio que hay? ¿O todo es cambio continuo y adaptación?
¿No hay un marco o unos límites estrictos? Entonces tenemos que valorar cómo de válida es esa declaración, hasta qué punto respeta los criterios y los límites y cuestionarnos, dar un paso atrás y cuestionarnos entonces cuál es la verdad. La verdad es la nota que toma el camarero o camarera sobre lo que uno quiere, lo que el cliente, el comenzar quiere, o las marcas que hace en esa nota en cocina sobre lo que ya está o lo que no está.
O cómo a partir de eso se gestiona el ticket de pago. Hay una verdad que va mutando en cada proceso, no es estática. La verdad depende de quién lo mire y quién la use en cada momento. Hay cosas que son relevantes y cosas que no lo son.
Como en un sistema neuronal hay neuronas que le prestan atención a unas cosas y a otras no. Son eventos que no, aunque ven, no consideran que son relevantes o que tienen que participar en ellos.
Estamos hablando de validar un pedido o de validar una infraestructura, como se hacía antes, si hemos encontrado algún tipo de estándar, si ese estándar tiene una capacidad suficientemente adaptativa en los diferentes fases de todo el proceso, están definidas claramente las fases o las fases pueden cambiar dependiendo de lo que ocurra en los diferentes trayectos o procesos.
Y más aún, finalmente hemos logrado aprender algo de toda esta dinámica para poder evitar errores en un momento dado y automatizar y facilitar todos los procesos, trayectos o viajes desde la entrada hasta la salida. ¿Cómo medimos esto? ¿Y qué valor tiene? ¿Qué peso tiene? ¿Qué repercusión tiene un fallo en un momento diferente, en un paso, en una situación diferente? No es lo mismo un fallo en la mesa que en la cocina, que la caída en un plato cuando se va a entregar la comida, que un desacuerdo entre lo que se pedido y lo que recibe en mesa cuando se entrega el plato, que una equivocación porque se cedido un tiempo de cocina en una elaboración o porque esa elaboración no contemplado diferentes elementos que se han asumido como parte de la historia.
Hasta qué punto hemos logrado customizar los procesos. Es decir, si yo hago siempre el mismo tipo de tareas o si el cliente es el que ya conozco, asumo cosas que tengo que registrar. Porque puede haber elementos o procesos en la cadena que no conozcan al cliente. El camarero sí lo conoce, pero en cocina no.
Entonces, para que haya este rigor, tengo que encontrar métodos que me aseguren que la fórmula es comprensible o que lo que se quiere es válido para cualquiera de los elementos que participan en la cadena. Y si alguno de estos elementos, llamémosles actores, o elementos que interactúen o estaciones del recorrido, pueda cambiar parte de lo definido y que eso tenga que volver a hacerse una validación al cliente.
Es que no tengo champiñón. Puedo reemplazártelo por una verdura.
Cuando empezamos con el HTML era perfecto porque era una manera de encapsular o una manera de guardar, reflejar el contenido para poderlo propagar de manera que se pudiera renderizar en la misma manera en diferentes puntos. Y pensamos que teníamos que usar más lenguajes y que para combinar esto pues ya no íbamos a escribir más HTML porque era un rollo.
Y entonces usamos herramientas que escribían HTML, escribían CSS, Javascript y aparte interactuaban con los servicios de backend. ¿Qué ocurrió después? Después pensamos que esto en infraestructura no era muy válido porque en mucha de la infraestructura pues lo que hacía era que dentro del paquete pues tenía todos los dependencias, todas las acciones, los PAD y tal, todo hardcoded.
Entonces pensamos en hacer de esto una configuración externa que pudiéramos declarar y que los programas o los instaladores pudieran usarla como modo de referencia donde poníamos los recursos de manera externa. Entonces como esto funcionaba muy bien entre máquinas utilizando JSON, pues el JSON se nos quedó como muy mal porque a muchos temas de sintaxis entonces pensamos que la mejor manera era utilizar un Tomele o un Yamele y que luego esto al cargarlo con herramientas, en por ejemplo Serde, el crate Serde, y así pues sería validado.
Y que entonces pues aceptaríamos elefante como animal de compañía o no. Y esto lo pondríamos a fuego dentro de la aplicación para que se ejecutara o no.
¿Y qué pasó? Que nos cansamos de escribir yameles y que era muy estresante mantener los espacios, mantener una cuestión de lo que es la sintaxis, etc. Y entonces utilizamos gestores de plantillas para que fueran capaces de escribir el yamele. Entonces lo que hacíamos es que le pasábamos una serie de variables, otra vez en yamele, domele o json, y era capaz de generar el yamele final, que era el que consumía la aplicación.
¿Todo esto valida el contenido del yaml? No, en absoluto. Es como ahora con la IA un markdown. En vez de escribir un prompt le paso un markdown o utilizo un markdown de referencia. Está validado su contenido. Puede ser sí o no o puede ofrecer una validación determinada como haríamos en un testing. No.
Entonces, nos movemos en ciclos de cambios, ciclos cambiantes, C y CD, de forma continua, pero ¿qué es lo que queremos? ¿Cuáles son las reglas de juego? ¿Y cuáles las acciones y los cambios que puedo introducir en esas reglas sin salirme fuera de contexto? sin salirme fuera, sin irme demasiado lejos de lo que quiero. Es decir, cuando tengo que volver a la mesa para preguntarle al comensal si le puedo cambiar determinada cosa en el plato porque no, esos ingredientes no son frescos de temporada. En realidad no estamos inventando nada, todo ya existe, el problema es si lo estamos gestionando adecuadamente.
¿Qué pasa cuando aparece RustS? Pues cuando aparece RustS introduce elementos que ya están o estaban en otros lenguajes, los reúne y aparece algo bastante diferente. En RustS hay mutabilidad, en RAS hay tipado estático, en RAS hay posibilidades de definir componentes o interfaces, como son los traits. Y entonces puedo validar implementaciones en función de los traits.
Tengo un compilador que analiza todo el código y el uso que se hace de los valores y variables antes de construir el binario.
¿Qué me ofrece RAS o cuál es la propuesta? En primer lugar, tengo que introducir un tipado, o sea, crear tipos para definir exactamente lo que quiero, para que mi declaración pueda interpretarse sin equívocos. Un número no es lo mismo que un texto o un string. RAS es enormemente exhaustivo en los diferentes tipos y tipos naturales del lenguaje o tipos primarios que ofrece.
En segundo lugar, es cohesivo y coherente con estos tipos, es decir, los valida de manera consistente, el tipado es estricto, no se relaja. Hay formas de pasar de un tipo a otro, de hacer las conversiones o los CAST. Hay métodos para gestionar la inmutabilidad, es decir, tengo que definir aquello que puede cambiarse o no de mi declaración.
Yo he dicho que quiero tomate y puedas inventar lo que quieras, pero si no tiene tomate no es el plato que he pedido, ¿vale? Entonces, estos condicionantes de por sí aparecen como invariantes, no pueden cambiar. Pero además tengo la opción de que haya opcionales, no nulos, no inventate lo que quieras si no tienes esto, no.
Hay algo que puedes ponerlo o no, ¿vale? Pero cuando no lo pones, puedo controlar que no está y cobrártelo o no cobrártelo.
En el análisis de los valores, para cargarlos en una implementación validando un esquema de tipos, yo puedo establecer restricciones, es decir, que un número sea un rango entre n y n, o que un valor sea dentro de una serie de valores determinados, como es el caso del enum.
Otro de los elementos importantes de Rust es la capacidad de definir funciones de llamada, sino analizar que esas funciones asociado determinado tipo de tipos y determinado tipo de implementaciones.Todos asociados a esa función.
Y luego tengo la posibilidad de definir traits, que ofrecen un sistema de composición, es decir, yo puedo componer implementaciones de un trait, variantes de un objeto en programación orientada a objetos, ¿vale? Que cumplen determinados tipos de criterios, normas o valores.
Es decir, hay una serie de formas de declarar lo que quiero, de instrumentos para declarar lo que uno quiere, ¿vale? Que son vigilados y mantenidos para que haya una coherencia entre lo que se quiere formular. ¿Quién es el vigilante o el que supervisa todo esto? El compilador. Pregunta, ¿cuándo lo hace?
Antes de construir el ejecutable. No cuando se está ejecutando. No cuando han pasado no sé cuánto tiempo de ejecución. No cuando toca llamar a una función a la que hace tiempo que no se llama. Es decir, el compilador intenta crear binarios o ejecutables predecibles en cómo se van a comportar con la memoria, con los recursos, con los valores y en los flujos de trabajo dentro de la propia aplicación.

View File

@ -0,0 +1,39 @@
Ahora vamos a explicar cómo resolvemos esto desde Rust sin mencionar nickel, pero usando sus conceptos, que luego decidiremos que es la opción para manejar los tipados de forma externa y por qué, pero esto lo explicamos más tarde.
En primer lugar necesitamos un esquema, es decir, necesitamos tener un patrón que determine que sirva de contenedor para lo que queremos declarar. En este caso, una forma de escribir, tener las opciones del menú para que se puedan elegir o no, unas estructuras en RASP, un esquema donde definimos que la cosa tiene, está asociada, cada elemento, cada ítem está asociado a un tipo, a un valor, un número, un string, un enum, etc.
Junto a este elemento podemos asociarle unos valores por defecto, ¿vale? De manera que cuando no se proporciona un valor, utilice un valor por defecto. El tercer elemento es un constraint, una limitación dentro del tipo. Por ejemplo, en un puerto puedo usar un rango específico.
En un plato puedo determinar tiene que ser un tipo de verdura de temporada, no solamente vegetal.
Ahora introducimos dos elementos más. El primero es un validador, es decir, en mi declaración puedo introducir un validador, ¿vale? Es decir, dentro del rango de precios, dentro de lo que es posible en cocina, utilizo una función que valida que lo que estás introduciendo es un número entre un valor u otro, que ese número se corresponde con otro valor o con otro ítem dentro de la misma declaración.
Los validadores los ponemos todos juntos y los podemos aplicar a diferentes declaraciones, estructuras o esquemas, al final son funciones con una entra y una salida.
El segundo elemento es la composición, es decir, yo puedo sobreescribir o cambiar determinado conjunto o implementación de valores por defecto, puedo utilizar determinadas definiciones generales o globales, heredarlas y cambiarlas.
Algo que podemos usar en Rust con funciones públicas o privadas y usando Traits.
¿Cuándo se valida todo esto? ¿Quién lo valida y con qué? Evidentemente no cuando el pedido ya llegado a cocina, sino antes de salir de la mesa. Incluso ya el comensal sabe lo que puede y lo que no puede hacer. Están las reglas preestablecidas. Falla en el tiempo de compilación, en el tiempo de construcción de la comanda o de la petición.
Falla en el momento en el que se articula lo que se quiere.
Esto es lo que elaboré en mi primera herramienta de despliegue en Rust CloudMandala en 2021.
Usando el crate de Serde y parsing de valores cargados vía configfiles o environment values.
¿ Es correcto, es suficiente, es eficiente ? Sí, pero no. Sí en sí mismo, pero no, porque los mecanismos de despliegue cada vez se requiere que sean más ágiles, con iteración más rápida, los ciclos son cada vez más frecuentes. Si lo hago en RUST, en el momento en el que cambio una definición, un tipo, una declaración, una limitación, constraint o restricciones de valor, tengo que volver a recompilar el target de RUST y tengo que distribuir este target a la arquitectura que corresponda y en el lugar en el que tiene que estar para cumplir esta validación y seguir con los procesos.
Es decir, esto me somete a que los ciclos de cambio y de adaptación son mucho más largos de lo que en sí demandan la CD/CI actual. En definitiva, es eficiente, pero no es suficientemente ágil. Entonces, ¿cómo puedo mantener estas funcionalidades sin Rust o de otra manera? Y para eso pensamos en usar lo que llamamos REPL, que son Read Evaluate Print Loop, es decir, programas que se ejecutan y que realizan determinado tipo de tareas de manera iterativa, en principio use Dhall (que está en Haskel) al final he utilizado KCL y finalmente Nickel que están escritos en Rust. Todos ellos permiten utilizar imports incluso desde repos git de bibliotecas de globales, de esquemas, utilizar defaults, utilizar constraints de valores mediante funciones con programación funcional, y además permiten composición y merge de valores. Su Su responsabilidad no va más allá de producir algo consistente y exportar en diferentes formatos de consumo, como JSON, Yaml o Toml. Esto significa que los targets que cargan estos ficheros, incluso tengo la carga directa desde ficheros NCL, que son los ficheros del tipo nickel, no tengan que hacer una segunda validación o una segunda consideración de los valores definidos en las declaraciones de forma exhaustiva. Estos REPL usan ficheros editables que puedo cambiar sobre la marcha de manera AGIL.
¿Es esto suficiente? No. ¿Por qué? Porque tener la declaración no significa que el camarero haya llevado la comanda a la cocina y que haya traído los platos a la mesa. Para eso necesito procedimientos, controles, ejecuciones, una serie de gestión de todos estos recorridos y tareas. ¿Lo puedo hacer esto en Rust?
Sí, puedes hacer llamadas al sistema, puedes hacer controles, etc. Pero esto también tiene que ser ágil, es decir, tengo que poder cambiar los métodos y procedimientos con agilidad para reaccionar en el caso de que se caiga un plato por el camino, en el caso de que ocurra cualquier contingencia que cambie el plan de acción. Y para resolver esto, en vez de Rust, volvemos a usar scripts y volvemos a usar templates con engines tipo el crate de Tera. Nushel es una shell escrita en Rust, otro REPL que podemos usar pegamento o seguimiento o definir interacciones sobre la marcha, de una manera mucho más eficiente que un script normal, porque sigue los patrones de Rust de forma bastante intensiva.
Es más, falla si no es capaz de cargar el código al inicio. Nushell tiene bucles "for par" para ejecutar acciones o llamadas en paralelo, por ejemplo, llamar a la CLI un proveedor para crear varias instancias a la vez en paralelo.
Entonces, ¿para qué necesito RAS? Para nada. Entonces, ¿lo resuelvo todo con REPLs? No. Aquí es donde vuelvo de nuevo a RAS. ¿Por qué? Porque RAS ofrece una serie de capacidades de trabajo con multi-threads seguras que no están al alcance de la mayor parte de los lenguajes, scripts y demás que pueda utilizar en infraestructura.
Eso hace que todas estas acciones con proveedores, ejecución de scripts que se han generado combinando valores declarados con plantillas, se realicen a través de un target en Rust que funciona como un orquestador. Él es capaz de ejecutar en paralelo, guardar valores seguros, recoger los retornos de resultado en las ejecuciones, realizar notificaciones de éxito o fracaso, reconducir o relanzar scripts que se predefinen para tareas alternativas e incluso rollback a estados previos, actua como un control-plane con gestion de estados y notificaciones.
Es capaz de conectarse con vaults para obtener de forma los credenciales de acceso vía API a un proveedor, etc.
Es suficiente para tener una maquinaria ¿Qué es la infraestructura? Es que la infraestructura no solamente son instancias, servidores, redes y demás, son también servicios que se declaran dentro de esa infraestructura, son configuraciones de estos servicios. Queremos que todo esté declarado y que sea elegible. No es primero este para crear instancias y luego aqué en Python con scripts shells para controlar la instalación de los paquetes, instalar la configuración de los servicios dentro de cada uno, bla, bla, bla.
Como casi todo requiere de Infraestructura, de componentes que sean SOLID que puedan iteractuar con una declaración que se va adaptando a lo que va sucediendo ... Pero entonces, ¿dónde está la verdad? Mejor preguntémonos previamente qué es lo que entendemos por verdad. Si es algo inmutable, una especie de ley que fue declarada, escrita en el libro sagrado, ¿cómo vamos a darle credibilidad a esto? Y cómo se va a ir adaptando a lo que vamos necesitando en cambios sucesivos, ágiles, pero que mantienen una coherencia y una credibilidad a pesar de una dinámica continua. Rust no es muy puro ¿ tal vez porque admite y contempla modos de mutar a pesar de empezar con un enfoque inmutable ? (Aquí svg animado de los recursos de Rust para mutar y gestionar la inmutabilidad).
Aquí mostramos la cosmología/arquitectura de Provisioning https://provisioning.systems/architecture-diagram.html

View File

@ -0,0 +1,109 @@
# Quick Start
## Installation & Run
```bash
cd /Users/Akasha/personal/0-Jobs/rustikon-2026-slides
npm install
npm run dev
```
Then open http://localhost:3030
## Edit Slides
Edit `slides.md` in your editor. Changes hot-reload automatically.
## Key Commands During Presentation
- `f` - Fullscreen
- `p` - Presenter view with notes
- `g` - Go to slide number
- `o` - Overview mode
- `ESC` - Exit presentation
## Export
```bash
# Export as PDF
npm run export:pdf
# Export as PNG (one per slide)
npm run export:png
# Build static HTML
npm run build
```
## Theme
Custom dark theme with Rust orange is in `theme/dark-rust.css`
Modify colors:
- Rust orange: `#CE422B`
- Background: `#0F0F0F`
- Text: `#E8E8E8`
## Add Images
1. Place images in `public/` directory
2. Reference in slides.md: `![Alt](/image.png)`
## Slide Syntax
See [Slidev docs](https://sli.dev/guide/syntax.html) for full syntax.
Quick examples:
```markdown
---
layout: cover
---
# Title Slide
---
layout: two-cols
---
# Left
Content
::right::
# Right
Content
---
layout: section
---
# Section Title
---
Regular slide with **bold**, *italic*, `code`
- Bullet points
- With nesting
- Level 2
```
## Speaker Notes
Notes at the bottom of each slide:
```markdown
---
layout: default
---
# Slide Title
Slide content
<style>
/* Slide-specific CSS */
</style>
```
Press `p` during presentation to see presenter view with notes.
Done! Happy presenting. 🦀

View File

@ -0,0 +1,114 @@
# Rustikon 2026 Presentation
**Why I Needed Rust, Finally: Infrastructure Automation I Can Sleep On**
Presentation slides built with [Slidev](https://sli.dev/)
## Setup
### Prerequisites
- Node.js 16+ (or Deno/Bun)
- npm or pnpm
### Installation
```bash
cd rustikon-2026-slides
npm install
```
### Development
Run the development server with hot reload:
```bash
npm run dev
```
Open `http://localhost:3030` in your browser.
### Build
Build slides for distribution:
```bash
npm run build
```
Output will be in `dist/`
### Export
Export as PDF:
```bash
npm run export:pdf
```
Export as PNG (per slide):
```bash
npm run export:png
```
## Structure
```
.
├── slides.md # All slides content
├── slidev.config.ts # Slidev configuration
├── theme/
│ └── dark-rust.css # Custom dark theme with Rust branding
├── public/ # Static assets (images, etc.)
└── package.json
```
## Customization
### Theme
The presentation uses a custom dark theme with Rust orange accents (`#CE422B`).
Edit `theme/dark-rust.css` to customize colors, fonts, or styles.
### Content
All slide content is in `slides.md`. Follow [Slidev markdown syntax](https://sli.dev/guide/syntax.html).
### Images
Place images in `public/` directory and reference them in slides.md:
```markdown
![Alt text](/image-name.png)
```
## Presentation Notes
Speaker notes are included at the end of `slides.md` under each slide's frontmatter or in the notes section.
Press `p` during presentation to see presenter view with notes.
## Keyboard Shortcuts
- `f` — Fullscreen
- `p` — Presenter view
- `g` — Go to slide
- `o` — Overview
- `j/k` — Next/previous slide
- `ESC` — Exit presentation
## Deployment
To host on GitHub Pages:
1. Build the slides: `npm run build`
2. Push `dist/` to your GitHub Pages branch
Or use Slidev's built-in deployment options.
## Author
Jesús Pérez Lorenzo
Rustikon 2026

View File

@ -0,0 +1,86 @@
## Nickel y Nushell — sí, pero con cuidado
Primero el diagnóstico honesto:
```
Rust → la audiencia es Rust — terreno seguro
Nickel → muy pocos en la sala lo conocerán
Nushell → algunos lo conocen, mayoría no
```
El riesgo es que suenen como stack personal en lugar de solución general. La oportunidad es exactamente esa rareza — **tú los usas en producción cuando casi nadie lo hace todavía**.
---
## Lo que cambiaría en el slide de Nickel
Ahora dice:
> *"YAML rejected. TOML rejected. Reason: no type safety."*
Eso es defensivo. Empieza por la solución, no por el rechazo:
```
Antes: "YAML rejected. TOML rejected."
(suena a preferencia personal)
Después: "Config is code. If it's code,
it deserves a type system."
(suena a principio)
```
Y añadir una línea que conecte con el DAG:
> *"Nickel is where the DAG is defined. Rust is where it runs. The compiler validates both."*
---
## Nushell — actualmente casi invisible
La charla lo menciona en los PDFs de tu proyecto pero no aparece como concepto en los slides. Eso es una pérdida porque Nushell es **el puente** — el lugar donde el DAG definido en Nickel se convierte en ejecución Rust. Sin ese puente, la historia tiene un agujero.
Un slide corto o una adición al slide de Nickel:
```
Nickel → defines what is valid (compile time)
Nushell → orchestrates what happens (the bridge)
Rust → executes with guarantees (runtime)
Three layers. Each catches what the previous can't.
```
Eso no necesita explicar qué es Nushell en detalle. Solo su rol en la cadena.
---
## La optimización más importante de todas
Ninguna de estas. Es esta:
**El slide de los metros — 🛡 😴 🔥 — aparece por primera vez en Stage 1 pero nunca se explica.**
Si lo explicas en 10 segundos al inicio:
> *"Three metrics. Security. Sleep. Fire-fighting. Watch them move."*
Y luego dejas que evolucionen solos a lo largo de la charla, tienes un hilo visual que hace todo el trabajo emocional sin palabras. El momento donde 🔥 baja de ●●●●● a ●○○○○ después de Rust es el momento más poderoso de la charla — pero solo si la audiencia sabe desde el principio lo que están viendo.
---
## Prioridad de cambios
```
Impacto alto, esfuerzo mínimo:
1. Explicar los metros al inicio — 10 segundos
2. DAG — tres menciones quirúrgicas ya descritas
3. Nickel: cambiar "rejected" por el principio
Impacto medio, algo de trabajo:
4. Nushell visible como puente — añadir al slide de Nickel
5. "More years" con la segunda mitad
Dejar como está:
6. Todo lo demás — no tocar lo que funciona
```
> El mayor riesgo de una charla buena no es mejorarla — es sobre-editarla. Estos cinco cambios tienen retorno claro. Más allá de aquí, el rendimiento decreciente aplica.

View File

@ -0,0 +1,117 @@
---
theme: default
#theme: ./themes/rust-vibe
title: Why I Needed Rust
titleTemplate: '%s - StratumIOps'
layout: cover
keywords: Rust,programming
download: true
exportFilename: StratumIOps Ontology and Reflection
monaco: true
remoteAssets: true
selectable: true
record: true
colorSchema: dark
#colorSchema: white
lineNumbers: false
themeConfig:
primary: '#f74c00'
logoHeader: '/ferris.svg'
fonts:
mono: 'Victor Mono'
drawings:
enabled: true
persist: false
presenterOnly: false
syncAll: true
scripts:
- setup/image-overlay.ts
#background: ./images/charles-assuncao-1BbOtIqx21I-unsplash.jpg
class: 'justify-center flex flex-cols photo-bg'
---
<h1 class="font-medium"> Ontology and Reflection </h1>
<h2 class="flex justify-center mt-5 font-medium">
StratumIOps
</h2>
<div class="flex justify-center">
<img class="mt-10 w-45" src="/jesusperez_w.svg">
</div>
<style scoped>
h1, h2, p { z-index: 10}
.meters {
margin-top: 2rem !important;
display: flex;
gap: 2 rem;
font-size: 1rem;
opacity: 0.6 !important;
font-family: monospace;
}
.two-cols {
column-gap: 1.5rem !important;
}
</style>
<Footer />
<!--
Note:
-->
---
layout: cover
name: end
#background: ./images/cleo-heck-1-l3ds6xcVI-unsplash.jpg
class: 'photo-bg'
---
<div class="standalone-slide text-shadow-lg text-xl perspective">
# Why I Needed Rust
<div class="meters-final -mt-3 mb-1">
<span>🛡 ●●●●●</span>
<span>😴 ●●●●●</span>
<span>🔥 ●○○○○</span>
</div>
<div class="text-xl leading-11 text-gray-200"> Three Closing Lines</div>
<div class="text-2xl leading-11 text-orange-200">
I have lived this problem for decades.<br>
Rust gave me deterministic systems and better sleep.<br>
Start small: model infrastructure as types.
</div>
<div class="text-5xl leading-12 text-gray-200 my-5">
Thank you. Questions?
</div>
<small>More info: </small>
· **jesusperez.pro** <br>
<small> · **provisioning.systems** · **vapora.dev** · **rustelo.dev** </small>
</div>
<style>
.meters-final {
display: inline-flex;
gap: 2rem;
font-size: 1.4rem;
font-family: monospace;
}
</style>
<!--
Final slide. Three meters in green.
The title of the talk, demonstrated.
Direct eye contact. Slow delivery.
-->

View File

@ -0,0 +1,22 @@
Why I Needed Rust: Finally, Infrastructure Automation I can sleep on
Description
In 2013, I started my journey with containers and deployment automation. I've encountered fundamental problems with infrastructure code: configuration sprawl, brittle scripts, complex state management, and deployments that fail silently in production, years of nightmares trying to solve it the wrong way.
Thanks to Rust. Now I finally sleep well.Why I Needed Rust: Finally, Infrastructure Automation I can sleep on
Description
This talk shares how.
Learn from decades of infrastructure experience:
• The evolution of infrastructure challenges (2013-2025)
• Why traditional approaches to IaC fall short at scale
• Type safety and memory safety as production reliability
• Building safe orchestration across multi-cloud and on-prem environments
• Real applications: Kubernetes, blockchain validators, disaster recovery
We'll explore concrete examples of what went wrong, why it went wrong, and how the Rust ecosystem can help solve infrastructure problems at scale.
For infrastructure engineers, SREs, platform teams, and Rust developers who've felt the pain of configuration drift, failed deployments, and infrastructure code that simply doesn't scale safely.
Suitable for: All experience levels. No advanced Rust knowledge required, this is about infrastructure problems and how Rust can help solve them.

View File

@ -0,0 +1,11 @@
¿Cómo quieres enfocar la reducción de texto?
1. Revisión completa
Repaso todas las slides y propongo qué cortar en cada una — tú apruebas antes de aplicar
2. Slides específicas
Me dices qué slide o sección te preocupa más y empezamos por ahí
3. Regla general
Aplico una regla directa: máximo 4 bullets por slide, blockquotes a una línea, sin texto explicativo redundante
4. Type something.
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
5. Chat about this

View File

@ -0,0 +1,198 @@
<template>
<div>
<!-- Image overlay toggle with M key -->
<transition name="fade">
<div
v-if="showImageOverlay"
class="fixed bottom-4 right-1 cursor-pointer pointer-events-auto w-auto z-200"
@click="toggleImageOverlay"
>
<div class="relative">
<img
src="/jpl-image.svg"
alt="JPL Image"
class="p-1 bg-gray-400 w-80 h-auto shadow-lg rounded-lg hover:shadow-xl transition-shadow"
/>
<div
class="absolute -bottom-8 left-1/2 transform -translate-x-1/2 text-xs text-gray-400 whitespace-nowrap pointer-events-none"
></div>
</div>
</div>
</transition>
<div
class="slide-footer abs-br m-0 px-4 pt-2 pb-0 flex gap-3 items-center"
>
<!-- Iconos sociales -->
<a
href="https://jesusperez.pro"
target="_blank"
alt="Website"
class="text-lg slidev-icon-btn opacity-50 !border-none !hover:text-white"
>
<carbon:earth-europe-africa />
</a>
<a
href="https://github.com/jesusperezlorenzo"
target="_blank"
alt="GitHub"
class="text-lg slidev-icon-btn opacity-50 !border-none !hover:text-white"
>
<carbon-logo-github />
</a>
<!-- Botón Projection Mode - SIN fondo -->
<button
@click="toggleProjectionMode"
class="projection-btn"
:class="{ 'projection-active': isProjectionMode }"
:title="
isProjectionMode
? 'Modo Proyección ON (P/D)'
: 'Modo Proyección OFF (P/D)'
"
>
<span class="text-base">{{
isProjectionMode ? "📽️" : "🖥️"
}}</span>
</button>
<!-- Texto autor y versión -->
<span class="footer-text">Jesús Pérez · 2025 v1.1</span>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue";
const isProjectionMode = ref(false);
const showImageOverlay = ref(false);
const toggleProjectionMode = () => {
isProjectionMode.value = !isProjectionMode.value;
if (isProjectionMode.value) {
document.documentElement.classList.add("projection-mode");
localStorage.setItem("rust-vibe-projection", "true");
} else {
document.documentElement.classList.remove("projection-mode");
localStorage.setItem("rust-vibe-projection", "false");
}
};
// Toggle image overlay
const toggleImageOverlay = () => {
showImageOverlay.value = !showImageOverlay.value;
};
// Atajos de teclado P, D, y M
const handleKeyPress = (event: KeyboardEvent) => {
const target = event.target as HTMLElement;
// Ignore if typing in input or textarea
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
return;
}
// Handle P and D keys for projection mode
if (
event.key === "p" ||
event.key === "P" ||
event.key === "d" ||
event.key === "D"
) {
event.preventDefault();
toggleProjectionMode();
}
// Handle M key for image overlay toggle
if (event.key === "m" || event.key === "M") {
event.preventDefault();
toggleImageOverlay();
}
};
// Sync with localStorage on mount
onMounted(() => {
const saved = localStorage.getItem("rust-vibe-projection");
if (saved === "true") {
isProjectionMode.value = true;
document.documentElement.classList.add("projection-mode");
}
// Añadir listener de teclado global
window.addEventListener("keydown", handleKeyPress);
});
// Cleanup on unmount
onUnmounted(() => {
window.removeEventListener("keydown", handleKeyPress);
});
</script>
<style scoped>
/* Image overlay fade transition */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.fade-enter-to,
.fade-leave-from {
opacity: 1;
}
.slide-footer {
opacity: 0;
transition: opacity 0.3s ease;
z-index: 10;
}
.slide-footer:hover {
opacity: 1;
}
.footer-text {
font-size: 0.65rem;
/* Más pequeño que text-xs */
opacity: 0.5;
}
.projection-btn {
opacity: 0.5;
transition: all 0.3s ease;
background: none !important;
/* SIN fondo, usa el de la slide */
border: none !important;
padding: 0.25rem;
cursor: pointer;
}
.projection-btn:hover {
opacity: 1;
transform: scale(1.15);
}
.projection-btn.projection-active {
opacity: 1;
/* Sin background para que use el de la slide */
}
/* Ocultar en modo presentador */
.slidev-presenter .slide-footer {
display: none;
}
/* Ocultar al imprimir */
@media print {
.slide-footer {
display: none;
}
}
</style>

View File

@ -0,0 +1,382 @@
# Componentes Personalizados para Slidev
## ProjectionToggle - Toggle para Modo Proyector
### 📍 Ubicación
`slides/components/ProjectionToggle.vue`
### 🎯 Descripción
Botón flotante para cambiar entre modo Monitor (oscuro) y modo Proyector (más claro) durante la presentación.
### ✨ Características
1. **Toggle visual**: Botón flotante con iconos 🖥️ / 📽️
2. **Persistencia**: Guarda la preferencia en localStorage
3. **Feedback visual**: Cambia de color según el modo activo
4. **No invasivo**: Se oculta automáticamente en modo presentador y al imprimir
5. **CSS Variables**: Usa variables CSS para cambios instantáneos
### 📝 Uso
Agrégalo a tu primera slide o a todas las slides principales:
```vue
<ProjectionToggle />
```
O en markdown:
```markdown
---
# Primera Slide
---
<ProjectionToggle />
# Tu contenido aquí...
```
### 🎨 Estados Visuales
**Monitor Mode** (por defecto)
- Icono: 🖥️ Monitor
- Color: Naranja (gradiente Rust)
- Fondo: #1c1c1c (muy oscuro)
**Projection Mode**
- Icono: 📽️ Projection
- Color: Verde (#48bb78)
- Fondo: #2d3748 (dark pero más visible)
### 💡 ¿Por qué dos modos?
Los proyectores tienen menor contraste que los monitores. El modo Projection:
- Fondo más claro (#2d3748 vs #1c1c1c)
- Mayor contraste de texto
- Bordes más gruesos en componentes
- Mejor visibilidad en salas con luz ambiente
### 🔧 Cómo Funciona
1. Click en el botón
2. Agrega/quita la clase `projection-mode` al `<html>`
3. Las CSS variables cambian automáticamente:
```css
:root {
--bg-primary: #1c1c1c; /* Monitor */
}
.projection-mode {
--bg-primary: #2d3748; /* Projection */
}
```
4. Guarda la preferencia en `localStorage`
### 📖 Documentación Completa
Ver: `slides/PROJECTION_MODE.md` para guía detallada sobre:
- Todos los métodos de activación
- Comparación visual
- Personalización de colores
- Troubleshooting
### 🎯 Recomendación de Uso
Antes de tu presentación:
1. Agrega `<ProjectionToggle />` a la primera slide
2. Durante el soundcheck con el proyector, prueba ambos modos
3. El modo elegido se guardará automáticamente
---
## YearsCounter - Contador Animado de Años
### 📍 Ubicación
`slides/components/YearsCounter.vue`
### 🎯 Descripción
Componente SVG animado que muestra un contador de años pasando como hojas de calendario, desde 1 hasta el año actual.
### ✨ Características
1. **Animación de contador**: Los números pasan de 1 hasta 40 (en 2025)
2. **Efecto de hoja**: Animación de scale que simula una página de calendario volteándose
3. **Badge animado**: Fondo con gradiente verde y efecto de brillo pulsante
4. **SVG puro**: Más ligero y eficiente que componentes complejos
5. **Totalmente personalizable**: Props para ajustar año inicial y velocidad
### 📝 Uso
En cualquier slide de Slidev:
```vue
<YearsCounter :startYear="1985" :speed="80" />
```
### 🎛️ Props
| Prop | Tipo | Default | Descripción |
|------|------|---------|-------------|
| `startYear` | Number | 1985 | Año desde el que se cuenta |
| `speed` | Number | 80 | Velocidad en milisegundos por año (menor = más rápido) |
| `delay` | Number | 3000 | Delay en ms antes de iniciar la animación (después de que la slide sea visible) |
### 🔧 Ejemplos de Configuración
#### Más Rápido (50ms por año)
```vue
<YearsCounter :startYear="1985" :speed="50" />
```
#### Más Lento (150ms por año)
```vue
<YearsCounter :startYear="1985" :speed="150" />
```
#### Desde otro año (ej: experiencia con Rust)
```vue
<YearsCounter :startYear="2019" :speed="100" />
```
#### Con delay personalizado (iniciar 5 segundos después)
```vue
<YearsCounter :startYear="1985" :speed="80" :delay="5000" />
```
#### Sin delay (iniciar inmediatamente cuando sea visible)
```vue
<YearsCounter :startYear="1985" :speed="80" :delay="0" />
```
### 🎨 Personalización del Estilo
El componente usa estas características visuales:
1. **Badge Verde**:
- Gradiente: `rgba(72,187,120,0.2)``rgba(72,187,120,0.4)`
- Borde: `#48bb78` (verde Rust accent)
- Bordes redondeados: `15px`
2. **Número**:
- Color: `#4ade80` (verde brillante)
- Tamaño: `48px`
- Fondo oscuro con opacidad
3. **Animaciones**:
- **Pulso del badge**: 3 segundos, brillo entre 0.3 y 0.6 opacidad
- **Flip del número**: 0.3 segundos, scale de `1,1``1,0.3``1,1`
### 🖼️ Estructura del SVG
```svg
<svg width="300" height="120">
<!-- Badge con gradiente y animación -->
<rect with gradient + glow filter + pulse animation />
<!-- Emoji -->
<text>💻</text>
<!-- Contenedor del número -->
<g>
<rect (fondo oscuro) />
<text (número con animación de flip) />
</g>
<!-- Texto descriptivo -->
<text>años como desarrollador</text>
</svg>
```
### 🔄 Cómo Funciona la Animación
#### Detección de Visibilidad (Nuevo)
1. **IntersectionObserver**: Monitorea cuando la slide es visible (30% threshold)
2. **Activación única**: Solo se inicia la primera vez que aparece
3. **Delay configurable**: Espera 3 segundos (por defecto) antes de iniciar
4. **Compatible**: Funciona tanto en Slidev como en navegación normal
#### Secuencia de la Animación
1. **Slide visible**: IntersectionObserver detecta que el componente es visible
2. **Delay**: Espera el tiempo configurado (por defecto 3000ms)
3. **Inicio**: Comienza el contador desde 1
4. **Incremento**: Cada `speed` milisegundos, incrementa el contador
5. **Efecto flip**: Cada cambio de número aplica una animación de scale en el eje Y
6. **Fin**: Cuando alcanza el año objetivo (2025-1985=40), detiene el intervalo
**Ejemplo de timing completo**:
- Llegas a la slide 2 → espera 3 segundos → cuenta de 1 a 40 en 3.2 segundos = **6.2 segundos total**
### 💻 Código JavaScript
```javascript
const counterElement = ref(null) // Referencia al elemento DOM
const displayYear = ref(1)
const hasStarted = ref(false) // Prevenir múltiples inicios
const targetYear = new Date().getFullYear() - props.startYear // 2025 - 1985 = 40
const startAnimation = () => {
if (hasStarted.value) return // Solo iniciar una vez
hasStarted.value = true
// Esperar el delay configurado antes de iniciar
setTimeout(() => {
let year = 1
const interval = setInterval(() => {
if (year <= targetYear) {
displayYear.value = year
year++
} else {
clearInterval(interval)
}
}, props.speed)
}, props.delay)
}
onMounted(async () => {
await nextTick()
// Observar cuando el componente es visible
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting && !hasStarted.value) {
startAnimation()
observer.disconnect() // Dejar de observar después de iniciar
}
})
},
{ threshold: 0.3 } // 30% visible
)
observer.observe(counterElement.value)
})
```
### 🎬 Resultado
Cuando navegas a la slide:
1. **Espera 3 segundos** (delay configurable)
2. **Inicia en 1**
3. **Va incrementando** cada 80ms
4. **Animación de flip** en cada cambio
5. **Termina en 40** (años desde 1985 hasta 2025)
**Duración total**:
- Delay: 3 segundos
- Animación: 40 años × 80ms = 3.2 segundos
- **Total: 6.2 segundos**
### ✨ Comportamiento Inteligente
- ✅ **Solo se inicia cuando la slide es visible** (no al cargar la presentación)
- ✅ **Delay de 3 segundos** para dar tiempo al usuario a leer el título
- ✅ **Se inicia solo una vez** (aunque vuelvas a la slide, no se reinicia)
- ✅ **Compatible con navegación** hacia adelante y atrás en Slidev
### 📱 Responsive
El SVG se adapta automáticamente al contenedor y mantiene su aspecto.
### 🔄 Actualización Automática
El cálculo `new Date().getFullYear() - props.startYear` se hace al montar el componente, por lo que:
- En **2025**: muestra de 1 a 40
- En **2026**: mostrará de 1 a 41
- Y así sucesivamente
### 🎯 Implementación en el Slide
**Slide 2** - Introducción Personal:
```markdown
---
layout: intro
---
# Jesús Pérez Lorenzo
<div class="flex items-center gap-11 mt-8">
<div class="leading-8 opacity-80">
<YearsCounter :startYear="1985" :speed="80" />
<span class="orange">Rust</span> Developer & Cloud Architect<br>
...
</div>
</div>
```
### 🛠️ Modificar la Animación
Para cambiar el tipo de animación, edita la sección `<animateTransform>` en el componente:
```xml
<!-- Flip horizontal en lugar de vertical -->
<animateTransform
attributeName="transform"
type="scale"
values="1,1; 0.3,1; 1,1"
dur="0.3s"/>
<!-- Rotación -->
<animateTransform
attributeName="transform"
type="rotate"
values="0 120 75; 360 120 75"
dur="0.5s"/>
```
### 🎨 Cambiar Colores
Para cambiar el esquema de colores, modifica:
```xml
<!-- Verde → Naranja Rust -->
<stop offset="0%" style="stop-color:rgba(247,76,0,0.2)" />
<stop offset="100%" style="stop-color:rgba(247,76,0,0.4)" />
<!-- Color del número -->
<text fill="#ff6b4a">
<!-- Color del borde -->
<rect stroke="#f74c00" />
```
## 🚀 Crear Otros Contadores
Puedes reutilizar el componente para otras métricas:
```vue
<!-- Años con Rust -->
<YearsCounter :startYear="2019" :speed="100" />
<!-- Años en Cloud -->
<YearsCounter :startYear="2013" :speed="100" />
```
## 📦 Archivos del Componente
```
slides/
├── components/
│ ├── YearsCounter.vue ← El componente
│ └── README.md ← Esta documentación
└── slides.md ← Uso del componente
```
## 🔍 Troubleshooting
### El componente no se muestra
- Asegúrate de que el archivo esté en `slides/components/`
- Slidev detecta automáticamente componentes en esta carpeta
### La animación es muy rápida/lenta
- Ajusta el prop `:speed` (en milisegundos)
- Valores recomendados: 50-150ms
### El número no llega al año correcto
- Verifica que `startYear` sea correcto
- El cálculo es: `new Date().getFullYear() - startYear`

View File

@ -0,0 +1,154 @@
<template>
<div ref="counterElement" class="years-counter-badge">
<span class="emoji">💻</span>
<div class="counter-box">
<span class="number">{{ displayYear }}</span>
</div>
<span class="label"></span>
</div>
</template>
<script setup>
import { ref } from "vue";
import { onSlideEnter } from "@slidev/client";
const props = defineProps({
startYear: {
type: Number,
default: 1985,
},
speed: {
type: Number,
default: 80,
},
delay: {
type: Number,
default: 3000, // 3 segundos de delay por defecto
},
});
const displayYear = ref(1);
const animationKey = ref(0);
const targetYear = new Date().getFullYear() - props.startYear;
const hasStarted = ref(false);
let interval = null;
const startAnimation = () => {
if (hasStarted.value) return; // Solo iniciar una vez
hasStarted.value = true;
// Esperar el delay configurado antes de iniciar
setTimeout(() => {
let year = 1;
interval = setInterval(() => {
if (year <= targetYear) {
displayYear.value = year;
animationKey.value = year * 0.001; // Trigger animation
year++;
} else {
clearInterval(interval);
}
}, props.speed);
}, props.delay);
};
// Usar onSlideEnter de Slidev para iniciar la animación cuando la diapositiva es visible
onSlideEnter(() => {
startAnimation();
});
</script>
<style scoped>
.years-counter-badge {
display: inline-flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1.5rem;
/*background: linear-gradient(
135deg,
rgba(72, 187, 120, 0.15),
rgba(72, 187, 120, 0.25)
);
border: 2px solid rgba(72, 187, 120, 0.5);
*/
border-radius: 12px;
backdrop-filter: blur(10px);
box-shadow: 0 4px 20px rgba(206, 66, 43, 0.2);
font-size: 1rem;
animation: pulse 3s ease-in-out infinite;
}
.emoji {
font-size: 1.5rem;
line-height: 1;
}
.counter-box {
background: rgba(0, 0, 0, 0.3);
border-radius: 8px;
padding: 0.25rem 0.75rem;
min-width: 3rem;
text-align: center;
}
.number {
font-size: 2rem;
font-weight: bold;
/*color: #4ade80;*/
color: rgba(206, 66, 43);
line-height: 1;
display: inline-block;
animation: flip 0.3s ease-in-out;
}
.label {
font-size: 0.95rem;
font-weight: 600;
color: #e5e7eb;
white-space: nowrap;
}
@keyframes pulse {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.9;
transform: scale(0.98);
}
}
@keyframes flip {
0%,
100% {
transform: scaleY(1);
}
50% {
transform: scaleY(0.3);
}
}
/* Responsive adjustments */
@media (max-width: 768px) {
.years-counter-badge {
font-size: 0.875rem;
padding: 0.5rem 1rem;
gap: 0.5rem;
}
.emoji {
font-size: 1.25rem;
}
.number {
font-size: 1.5rem;
}
.label {
font-size: 0.8rem;
}
}
</style>

View File

@ -0,0 +1,142 @@
# Rustikon 2026 - Frases Criticas por Bloques (30 min)
Guia de control narrativo para saber si vas en tiempo o debes acelerar.
## Resumen de Bloques
| Bloque | Slides | Objetivo narrativo | Tiempo bloque | Acumulado |
|---|---:|---|---:|---:|
| B1 Hook & Promise | 1-3 | Abrir con credibilidad y dolor real. | 01:31 | 01:31 |
| B2 Escalation | 4-7 | Mostrar que la complejidad crece sin control. | 04:36 | 06:07 |
| B3 Problem Anatomy | 8-14 | Hacer evidente la causa raiz y el costo de fallar tarde. | 07:41 | 13:48 |
| B4 Turning Point | 15-18 | Giro narrativo: no faltan tools, falta paradigma. | 02:02 | 15:50 |
| B5 Resolution by Types | 19-23 | Conectar Rust/tipos con respuestas concretas. | 02:59 | 18:49 |
| B6 Proof in Production | 24-31 | Probar con casos reales e impacto operativo. | 08:32 | 27:21 |
| B7 Close & CTA | 32-35 | Cerrar con mensaje memorable y accion. | 02:39 | 30:00 |
## Señales de Ritmo
- Si en **10:00** no has llegado a Slide 12-13, acelera (reduce ejemplos y tablas).
- Si en **20:00** no has llegado a Slide 24, recorta explicacion tecnica y ve a casos reales.
- En **27:00** deberias estar entrando al cierre (Slide 32+).
## B1 - Hook & Promise (Slides 1-3)
Tiempo objetivo bloque: **01:31**
Objetivo: Abrir con credibilidad y dolor real.
Frases criticas por slide:
- Slide 1: Why I Needed Rust / Finally. / infra Automation I Can Sleep On
- Slide 2: Years. / One Problem. / Era Tool Lesson
- Slide 3: The Evolution / How we got here
Regla de enfasis:
- Habla lento. No vendas tecnologia; vende problema vivido.
## B2 - Escalation (Slides 4-7)
Tiempo objetivo bloque: **04:36**
Objetivo: Mostrar que la complejidad crece sin control.
Frases criticas por slide:
- Slide 4: Stage 1. / Local (late 80s / early 90s) / Dumb terminals. / Single machine. / One state.
- Slide 5: Stage 2. / Networks / Internet / Systems getting farther away. / More people. / More coordination.
- Slide 6: Stage 3. / Containers / Cloud / CI-CD / Everything. / Everywhere. / All at once.
- Slide 7: I could automate infra. / But I couldn't make it reliable.
Regla de enfasis:
- Sube energia y marca cada "lesson" con pausa corta.
## B3 - Problem Anatomy (Slides 8-14)
Tiempo objetivo bloque: **07:41**
Objetivo: Hacer evidente la causa raiz y el costo de fallar tarde.
Frases criticas por slide:
- Slide 8: Why I A C Fails / The restaurant problem
- Slide 9: The Restaurant / Every restaurant has at least three actors.
- Slide 10: The Truth That Mutates / State is not static. / It can change at every step of the chain.
- Slide 11: "We Don't Have Mushrooms" / When an actor in the chain can't fulfill part of the order.
- Slide 12: The Config Evolution / How we got from code to YAML hell
- Slide 13: Continuous C I slash C D. / No semantic check.
- Slide 14: Three Questions Without Answers / Fail late is expensive. Fail early is cheap.
Regla de enfasis:
- Maxima claridad. Una idea por frase.
## B4 - Turning Point (Slides 15-18)
Tiempo objetivo bloque: **02:02**
Objetivo: Giro narrativo: no faltan tools, falta paradigma.
Frases criticas por slide:
- Slide 15: The tools weren't the problem. / The languages weren't the problem.
- Slide 16: Systems we don't know how to control. / We hope they work.
- Slide 17: Rust / The answer to all three questions
- Slide 18: The Bridge: From Serde to Types / Serde loads structurally valid config.
Regla de enfasis:
- Haz silencio antes y despues del giro.
## B5 - Resolution by Types (Slides 19-23)
Tiempo objetivo bloque: **02:59**
Objetivo: Conectar Rust/tipos con respuestas concretas.
Frases criticas por slide:
- Slide 19: What Rust Gives Us / Answer to Question 1: fail early. / fail cheap
- Slide 20: The compiler check as Pre-Validator / Answer to Question 3:
- Slide 21: The Human Impact / When the system is trustworthy:
- Slide 22: Continuous C I slash C D. / Types. / compiler check. / Explicit state.
- Slide 23: In Production / This is not theory
Regla de enfasis:
- Tecnico, pero aterrizado a impacto operativo.
## B6 - Proof in Production (Slides 24-31)
Tiempo objetivo bloque: **08:32**
Objetivo: Probar con casos reales e impacto operativo.
Frases criticas por slide:
- Slide 24: Provisioning architecture (visual) / One source of truth, typed end to end.
- Slide 25: Nickel / YAML rejected. / TOML rejected. / Reason: no type safety.
- Slide 26: Traits as Provider / The kitchen can change. / AWS ≠ UpCloud ≠ bare metal. / Same menu.
- Slide 27: Dependency Graph / Failfast: bool is not a config option.
- Slide 28: Real Applications / Kubernetes + validators in production conditions.
- Slide 29: Disaster Recovery / Rollback as a type. / not a procedure
- Slide 30: Self-Healing / When something breaks at 3 AM, the system responds first.
- Slide 31: Without types. / Without compiler check. / Without explicit state. / MTTR > 30 minutes.
Regla de enfasis:
- Menos teoria, mas evidencia: MTTR, DR, 3AM.
## B7 - Close & CTA (Slides 32-35)
Tiempo objetivo bloque: **02:39**
Objetivo: Cerrar con mensaje memorable y accion.
Frases criticas por slide:
- Slide 32: Why This Matters / For everyone in this room
- Slide 33: For You / If you've been frustrated like me
- Slide 34: I have perspective from long production experience. / I have seen technologies come and go.
- Slide 35: Why I Needed Rust / Three Closing Lines
Regla de enfasis:
- Baja ritmo y cierra con conviccion.
## Checkpoints Rápidos
- 05:00 -> deberias estar en Slide 6
- 10:00 -> deberias estar en Slide 12-13
- 15:00 -> deberias estar en Slide 18-19
- 20:00 -> deberias estar en Slide 24
- 25:00 -> deberias estar en Slide 29-30
- 30:00 -> Slide 35 (end)

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -0,0 +1,47 @@
# Rustikon 2026 - Key Story Moments
Use this as your minimal narrative map for a 30-minute talk.
## Moment 1 - Hook (Slides 1-3)
Purpose: Win attention and establish credibility.
Anchor line:
"I have lived this problem for decades, and I wanted one thing: to sleep."
## Moment 2 - Rising Tension (Slides 4-7)
Purpose: Show complexity growing faster than control.
Anchor line:
"We changed tools many times, but not the model."
## Moment 3 - Crisis / Root Cause (Slides 8-14)
Purpose: Make the cost of late failure undeniable.
Anchor line:
"Fail late is expensive. Fail early is cheap."
## Moment 4 - Turning Point (Slides 15-18)
Purpose: Reframe the problem.
Anchor line:
"The problem was not the tools. The problem was the paradigm."
## Moment 5 - Proof in Production (Slides 24-31)
Purpose: Show real operational evidence.
Anchor line:
"At 3 AM, the system responds first. Not me."
## Moment 6 - Emotional Close + CTA (Slides 33-35)
Purpose: Close with trust and clear action.
Anchor line:
"Rust gave me deterministic systems and better sleep."
## One-Line Backup Version
1. I lived this problem for decades.
2. Complexity grew; certainty did not.
3. Late failures are the real tax.
4. Types changed the paradigm.
5. Production proof: lower MTTR and deterministic workflows.
6. Start small: model infrastructure as types.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,489 @@
---
theme: default
title: Why I Needed Rust
titleTemplate: '%s - Rustikon 2026'
layout: cover
---
# Why I Needed Rust
## Finally Infrastructure Automation I Can Sleep On
Jesús Pérez Lorenzo
Las Palmas, Spain
Rustikon 2026
---
# The Problem Statement
After decades in infrastructure, I was exhausted.
Not by the work.
By the failures.
<style>
h1 { font-size: 3rem; }
p { font-size: 1.5rem; color: var(--color-text-secondary); margin: 0.5em 0; }
</style>
---
layout: two-cols
---
# 38 Years of Infrastructure
**1987 — 2025**
::left::
- Perl (1990s)
- Python (2000s)
- Bash (2000s)
- Chef (2010s)
- Ansible (2010s)
- Terraform (2010s)
- Go (2010s-2020s)
- ???
::right::
> "I've tried everything to solve the provisioning problem."
Each time, I thought I had the answer.
Each time, reality proved me wrong.
---
# The Perl Era (1990s)
**Power and Chaos**
<div class="box-highlight">
<strong>The Promise:</strong> We could do anything
<strong>The Reality:</strong> We could break anything
</div>
- Metaprogramming that was beautiful and terrifying
- No safety net
- Silent failures at 3 AM
- **Lesson:** Power without safety is a disaster
---
# The Python Pragmatism (2000s)
**Flexible, Rapid, Trusted**
<div class="box-highlight">
<strong>The Appeal:</strong> Rapid development, great community
<strong>The Problem:</strong> Nothing stopped you from being wrong
</div>
- Type hints came late (and optional)
- Runtime errors > compile-time errors
- Scripts that "worked on my machine"
- **Lesson:** Pragmatism without guarantees is fragile
---
# The Cloud Scale (2010s)
**More Tools, Same Problems**
Tools arrived: **Ansible • Terraform • Chef • Puppet**
<div class="box-highlight">
<strong>What changed?</strong> The syntax
<strong>What didn't?</strong> The fundamental problems
</div>
- Still fighting type safety
- Still discovering errors in production
- Still maintaining undocumented scripts
- **Lesson:** More tools don't solve paradigm problems
---
# The Breaking Point
<div style="text-align: center; margin: 3em 0;">
**I could automate infrastructure**
**But I couldn't make it reliable**
**I couldn't prevent mistakes**
**I couldn't sleep**
</div>
---
layout: section
---
# The Real Problems
What Actually Fails
---
# What Actually Fails
- **Typos discovered at 3 AM** in production configs
- **Scripts that "worked on my machine"** fail everywhere else
- **Nobody understands the code** even 6 months later
- **Changes that break other things** silently
- **Secrets scattered everywhere** without audit trails
<div class="box-highlight">
It's not about <strong>deployment</strong>.
It's about <strong>reliable deployment</strong> that doesn't cause panic.
</div>
---
# The Cost
Not just technical:
- Hours debugging cryptic errors
- Sleep lost to on-call incidents
- Team stress and burnout
- Lack of confidence in deployments
- Technical debt that compounds
- **The thing you can't measure: fear**
---
# The Realization
The tools weren't the problem.
The languages weren't the problem.
**The paradigm was the problem.**
I needed something that:
- Forced clarity (not enabled chaos)
- Prevented errors (before production)
- Made assumptions explicit (not hidden)
- Gave confidence (not anxiety)
---
layout: section
---
# Rust Enters the Picture
The Shift
---
# First Encounter with Rust
<div class="box-highlight">
<strong>"Type safety?"</strong>
<strong>"Memory safety?"</strong>
<strong>"Do I really need this?"</strong>
This looks... complicated.
</div>
Initial reaction: skepticism mixed with curiosity
---
# The Shift: Type System as Defense
<div style="text-align: center; margin: 2em 0;">
**Wait.**
**The compiler could prevent my mistakes?**
**Before they reach production?**
**Before I even run the code?**
</div>
This changed everything.
---
# What Changed For Me
| Problem | Old Way | Rust Way |
|---------|---------|----------|
| Typos in config | 3 AM incident | Compile error |
| "Works on my machine" | Hope and prayers | Guaranteed same output |
| Secrets leaking | Runtime discovery | Type system prevents it |
| Undocumented constraints | Institutional knowledge | Compiler enforces it |
| Silent failures | Debug for hours | Explicit error handling |
---
# The Infrastructure Insight
<div class="box-highlight">
I could define constraints in code.
Compiler enforced them everywhere.
Changes forced adaptation everywhere.
No silent failures.
</div>
This is not a small thing.
---
# The Human Impact
When the system is trustworthy:
- ✓ Sleep comes back
- ✓ Confidence returns
- ✓ Team trusts the automation
- ✓ Stress decreases
- ✓ You can actually rest
---
layout: section
---
# What I Built
The Implementation
---
# Current Projects
**Building in Rust:**
- **project-provisioning** — Infrastructure provisioning orchestrator
- **secretumvault** — Secrets management with PQC
- **typedialog** — Interactive configuration forms
- **daemon-cli** — Unified service abstraction
Not selling products.
**Demonstrating that it works.**
---
# The Key Patterns
Type-safe declarations
- Schema defines constraints at compile-time
Schema-driven generation
- Forms, validation, templates all derived from one source
Distributed orchestration
- Control, not chaos. Semaphore-based coordination
Unified governance
- Auth, audit, compliance as first-class concerns
**Each pattern prevents specific failures.**
---
# Why Rust Specifically
<div class="box-highlight">
<strong>Type System</strong>
Prevents entire categories of errors before execution
<strong>Performance</strong>
No overhead, can be truly reactive
<strong>Ecosystem</strong>
Coherent tools (Nickel, Nushell, KCL) not disparate solutions
<strong>Single Binary</strong>
Deploy anywhere, no dependency hell
<strong>Community</strong>
People who care about correctness
</div>
---
layout: section
---
# What I Learned
The Philosophy
---
# Three Key Insights
**1. Infrastructure is your core, not an afterthought**
It's not a support function. It's the foundation your business runs on.
**2. Reliability requires preventing errors, not handling them**
You can't code around a broken paradigm.
**3. Engineers deserve better than anxiety**
We shouldn't be woken at 3 AM by infrastructure failures.
---
# The Age Advantage
<div class="box-highlight">
At 63, I have perspective.
I've seen technologies come and go.
<strong>Rust isn't hype. It solves real problems I've had for decades.</strong>
Being older isn't a liability.
It's an advantage.
</div>
I can tell you: this matters.
---
# What Infrastructure Actually Is
- **Core of your business** — not a cost center
- **Must evolve** with business needs
- **Can't be fragile** — customers depend on it
- **Can't cause panic** — engineers can't be on-call all the time
- **Engineers deserve to rest** — this is non-negotiable
---
layout: section
---
# The Ask
For Everyone Here
---
# For Infrastructure Veterans
If you've been frustrated like me:
- ✓ Listen up
- ✓ Rust solves the problems *you know*
- ✓ Not problems you've never had
- ✓ Give it a real chance
- ✓ Your sleep will thank you
---
# For Younger Engineers
Learn from someone who tried everything:
- ✓ Don't waste 30 years on fragile infrastructure
- ✓ Start with type safety
- ✓ Build for reliability from- day one
- ✓ You'll thank yourself later
---
# The Simple Ask
<div style="text-align: center; margin: 3em 0;">
**Think differently about infrastructure.**
Type safety isn't luxury.
It's foundation.
You deserve better than 3 AM panic.
</div>
---
layout: cover
---
# Why I Needed Rust, Finally
## Because I Wanted to Sleep
Questions?
---
# Resources & Contact
**Projects:**
- github.com/jesusperez/provisioning
- github.com/jesusperez/secretumvault
- github.com/jesusperez/typedialog
**Community:**
- Rust Las Palmas — rust-laspalmas.dev
**Contact:**
- jesusperez.pro
<style>
a { color: var(--rust-orange); }
</style>
---
# Speaker Notes
## Delivery Tips
- **Opening:** Vulnerable, honest. Show the struggle before the solution.
- **Tone shift:** Around slide "The Shift" — move from struggling to architect who figured it out.
- **Key moments:** Pause after emotional beats. Let them sink in.
- **Age:** Not an apology, not bragging. Just perspective.
- **Closing:** Hopeful, inclusive. This is for everyone.
## Audience Engagement
- **Opening question:** "Who's been woken up at 3 AM by infrastructure failure?" (show of hands)
- **Pause points:** After problem slides, let silence sit
- **Expression:** Show relief when talking about Rust solution
- **Ending:** Direct eye contact, slow delivery
## Timing
- 2 min: Opening (slides 1-2)
- 5 min: Journey (slides 3-8)
- 5 min: Problems (slides 9-12)
- 6 min: Rust discovery (slides 13-17)
- 5 min: Implementation (slides 18-20)
- 4 min: Philosophy (slides 21-23)
- 2 min: Call to action (slides 24-26)
- 1 min: Closing (slide 27)
- **Total: 30 minutes + Q&A**
## If Extending to 40 min
Add stories:
- Specific 3 AM incident (2 min)
- First Rust project struggles (2 min)
- Team adoption journey (2 min)
- Measurable improvements (2 min)

View File

@ -0,0 +1,20 @@
{
"name": "rustikon-2026-slides",
"version": "1.0.0",
"description": "Why I Needed Rust, Finally - Rustikon 2026",
"scripts": {
"dev": "slidev --port 4040",
"build": "slidev build",
"export": "slidev export",
"export:pdf": "slidev export --format pdf",
"export:png": "slidev export --format png"
},
"devDependencies": {
"@slidev/cli": "^52.2.5",
"@slidev/theme-apple-basic": "^0.25.1",
"@slidev/theme-default": "^0.25.0",
"@slidev/theme-seriph": "^0.25.0",
"@vueuse/core": "^13.9.0",
"@vueuse/shared": "14.0.0-alpha.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 695 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="1200" height="800" viewBox="0 0 1200 800" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<g id="Layer-1" serif:id="Layer 1">
<g transform="matrix(1,0,0,1,654.172,668.359)">
<path d="M0,-322.648C-114.597,-322.648 -218.172,-308.869 -296.172,-286.419L-296.172,-291.49C-374.172,-266.395 -423.853,-231.531 -423.853,-192.984C-423.853,-186.907 -422.508,-180.922 -420.15,-175.053L-428.134,-160.732C-428.134,-160.732 -434.547,-152.373 -423.199,-134.733C-413.189,-119.179 -363.035,-58.295 -336.571,-26.413C-325.204,-10.065 -317.488,0 -316.814,-0.973C-315.753,-2.516 -323.878,-33.202 -346.453,-68.215C-356.986,-87.02 -369.811,-111.934 -377.361,-130.335C-356.28,-116.993 -328.172,-104.89 -296.172,-94.474L-296.172,-94.633C-218.172,-72.18 -114.597,-58.404 0,-58.404C131.156,-58.404 248.828,-76.45 327.828,-104.895L327.828,-276.153C248.828,-304.6 131.156,-322.648 0,-322.648" style="fill:rgb(165,43,0);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,1099.87,554.94)">
<path d="M0,-50.399L-13.433,-78.227C-13.362,-79.283 -13.309,-80.341 -13.309,-81.402C-13.309,-112.95 -46.114,-142.022 -101.306,-165.303L-101.306,2.499C-75.555,-8.365 -54.661,-20.485 -39.72,-33.538C-44.118,-15.855 -59.157,19.917 -71.148,45.073C-90.855,81.054 -97.993,112.376 -97.077,113.926C-96.493,114.904 -89.77,104.533 -79.855,87.726C-56.783,54.85 -13.063,-7.914 -4.325,-23.901C5.574,-42.024 0,-50.399 0,-50.399" style="fill:rgb(165,43,0);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,1177.87,277.21)">
<path d="M0,227.175L-88.296,162.132C-89.126,159.237 -89.956,156.345 -90.812,153.474L-61.81,111.458C-58.849,107.184 -58.252,101.629 -60.175,96.755C-62.1,91.905 -66.311,88.428 -71.292,87.576L-120.335,79.255C-122.233,75.376 -124.225,71.557 -126.224,67.771L-105.62,20.599C-103.501,15.793 -103.947,10.209 -106.759,5.848C-109.556,1.465 -114.31,-1.094 -119.376,-0.895L-169.146,0.914C-171.723,-2.442 -174.34,-5.766 -177.012,-9.032L-165.574,-59.592C-164.415,-64.724 -165.876,-70.1 -169.453,-73.83C-173.008,-77.546 -178.175,-79.084 -183.089,-77.88L-231.567,-65.961C-234.707,-68.736 -237.897,-71.474 -241.126,-74.157L-239.381,-126.064C-239.193,-131.318 -241.643,-136.311 -245.849,-139.227C-250.053,-142.161 -255.389,-142.603 -259.987,-140.423L-305.213,-118.921C-308.853,-121.011 -312.515,-123.081 -316.218,-125.084L-324.209,-176.232C-325.021,-181.413 -328.355,-185.816 -333.024,-187.826C-337.679,-189.848 -343.014,-189.193 -347.101,-186.116L-387.422,-155.863C-391.392,-157.181 -395.38,-158.446 -399.418,-159.655L-416.798,-208.159C-418.564,-213.104 -422.64,-216.735 -427.608,-217.756C-432.561,-218.768 -437.656,-217.053 -441.091,-213.217L-475.029,-175.246C-479.133,-175.717 -483.239,-176.147 -487.356,-176.505L-513.564,-220.659C-516.22,-225.131 -520.908,-227.852 -525.961,-227.852C-531.002,-227.852 -535.7,-225.131 -538.333,-220.659L-564.547,-176.505C-568.666,-176.147 -572.791,-175.717 -576.888,-175.246L-610.831,-213.217C-614.268,-217.053 -619.382,-218.768 -624.318,-217.756C-629.284,-216.721 -633.363,-213.104 -635.124,-208.159L-652.517,-159.655C-656.544,-158.446 -660.534,-157.173 -664.514,-155.863L-704.822,-186.116C-708.92,-189.204 -714.254,-189.857 -718.92,-187.826C-723.57,-185.816 -726.917,-181.413 -727.723,-176.232L-735.72,-125.084C-739.42,-123.081 -743.083,-121.022 -746.734,-118.921L-791.956,-140.423C-796.548,-142.612 -801.908,-142.161 -806.091,-139.227C-810.292,-136.311 -812.747,-131.318 -812.557,-126.064L-810.821,-74.157C-814.04,-71.474 -817.224,-68.736 -820.379,-65.961L-868.849,-77.88C-873.774,-79.075 -878.935,-77.546 -882.499,-73.83C-886.084,-70.1 -887.538,-64.724 -886.384,-59.592L-874.969,-9.032C-877.618,-5.753 -880.239,-2.442 -882.808,0.914L-932.579,-0.895C-937.602,-1.043 -942.396,1.465 -945.202,5.848C-948.014,10.209 -948.439,15.793 -946.348,20.599L-925.729,67.771C-927.732,71.557 -929.721,75.376 -931.635,79.255L-980.675,87.576C-985.657,88.417 -989.858,91.892 -991.795,96.755C-993.72,101.629 -993.095,107.184 -990.156,111.458L-961.146,153.474C-961.37,154.215 -961.576,154.964 -961.799,155.707L-1043.82,242.829C-1043.82,242.829 -1056.38,252.68 -1038.09,275.831C-1021.95,296.252 -939.097,377.207 -895.338,419.62C-876.855,441.152 -864.195,454.486 -862.872,453.332C-860.784,451.5 -871.743,412.326 -908.147,366.362C-936.207,325.123 -972.625,261.696 -964.086,254.385C-964.086,254.385 -954.372,242.054 -934.882,233.178C-934.169,233.749 -935.619,232.613 -934.882,233.178C-934.882,233.178 -523.568,422.914 -142.036,236.388C-98.452,228.571 -72.068,251.917 -72.068,251.917C-62.969,257.193 -86.531,322.412 -105.906,365.583C-132.259,414.606 -136.123,452.859 -133.888,454.185C-132.479,455.027 -122.89,440.438 -109.214,417.219C-75.469,370.196 -11.675,280.554 0,258.781C13.239,234.094 0,227.175 0,227.175" style="fill:rgb(247,76,0);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,795.856,464.937)">
<path d="M0,159.631C1.575,158.289 2.4,157.492 2.4,157.492L-132.25,144.985C-22.348,0 65.618,116.967 74.988,129.879L74.988,159.631L0,159.631Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,278.418,211.791)">
<path d="M0,253.04C0,253.04 -111.096,209.79 -129.876,163.242C-129.876,163.242 0.515,59.525 -155.497,-50.644L-159.726,89.773C-159.726,89.773 -205.952,45.179 -203.912,-32.91C-203.912,-32.91 -347.685,36.268 -179.436,158.667C-179.436,158.667 -173.76,224.365 -22.459,303.684L0,253.04Z" style="fill:rgb(247,76,0);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,729.948,492.523)">
<path d="M0,-87.016C0,-87.016 41.104,-132.025 82.21,-87.016C82.21,-87.016 114.507,-27.003 82.21,3C82.21,3 29.36,45.009 0,3C0,3 -35.232,-30.006 0,-87.016" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,777.536,422.196)">
<path d="M0,0.008C0,17.531 -10.329,31.738 -23.07,31.738C-35.809,31.738 -46.139,17.531 -46.139,0.008C-46.139,-17.521 -35.809,-31.73 -23.07,-31.73C-10.329,-31.73 0,-17.521 0,0.008" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,546.49,486.263)">
<path d="M0,-93.046C0,-93.046 70.508,-124.265 89.753,-54.583C89.753,-54.583 109.912,26.635 31.851,31.219C31.851,31.219 -67.69,12.047 0,-93.046" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,581.903,423.351)">
<path d="M0,0.002C0,18.074 -10.653,32.731 -23.794,32.731C-36.931,32.731 -47.586,18.074 -47.586,0.002C-47.586,-18.076 -36.931,-32.729 -23.794,-32.729C-10.653,-32.729 0,-18.076 0,0.002" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,1002.23,778.679)">
<path d="M0,-296.808C0,-296.808 -14.723,-238.165 -106.292,-176.541L-131.97,-170.523C-131.97,-170.523 -215.036,-322.004 -332.719,-151.302C-332.719,-151.302 -296.042,-172.656 -197.719,-146.652C-197.719,-146.652 -242.949,-77.426 -334.061,-79.553C-334.061,-79.553 -246.748,25.196 -113.881,-126.107C-113.881,-126.107 26.574,-180.422 37.964,-296.808L0,-296.808Z" style="fill:rgb(247,76,0);fill-rule:nonzero;"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="4417" height="3259" viewBox="0 0 4417 3259" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<g transform="matrix(4.16667,0,0,4.16667,0,0)">
<path d="M525.403,293.05C393.77,293.05 274.175,308.875 185.633,334.665L185.633,554.963C274.175,580.753 393.77,596.577 525.403,596.577C676.06,596.577 810.938,575.848 901.537,543.175L901.537,346.457C810.938,313.781 676.06,293.05 525.403,293.05Z" style="fill:rgb(143,30,28);fill-rule:nonzero;"/>
<path d="M907.423,492.442C903.566,481.779 902.794,468.288 906.062,455.28C911.912,431.991 928.483,419.082 943.075,426.447C946.693,428.274 949.849,431.178 952.462,434.865C952.701,434.864 952.94,434.865 953.177,434.881C953.177,434.881 997.729,487.987 956.49,550.884C955.595,554.453 879.956,642.602 862.447,645.408C850.987,647.244 877.338,555.41 907.423,492.442Z" style="fill:rgb(143,30,28);fill-rule:nonzero;"/>
<path d="M176.479,482.021C181.779,472.391 183.637,459.233 180.696,445.596C175.43,421.18 156.786,404.486 139.054,408.311C134.656,409.259 130.729,411.383 127.388,414.409C127.106,414.351 126.824,414.296 126.543,414.256C126.543,414.256 70.251,456.208 114.486,528.18C115.291,531.921 198.337,637.018 218.797,643.943C232.188,648.475 207.55,551.418 176.479,482.021Z" style="fill:rgb(143,30,28);fill-rule:nonzero;"/>
<path d="M97.467,488.066L97.474,488.081C97.659,488.226 97.831,488.357 97.467,488.066Z" style="fill:rgb(227,58,37);fill-rule:nonzero;"/>
<path d="M993.119,412.903C992.239,409.839 991.363,406.777 990.457,403.741L1021.14,359.29C1024.27,354.768 1024.91,348.892 1022.87,343.735C1020.83,338.605 1016.38,334.925 1011.11,334.025L959.224,325.22C957.216,321.118 955.108,317.078 952.994,313.07L974.791,263.167C977.034,258.08 976.56,252.172 973.588,247.559C970.627,242.923 965.598,240.215 960.239,240.426L907.583,242.339C904.856,238.789 902.087,235.271 899.261,231.818L911.362,178.328C912.587,172.895 911.04,167.21 907.259,163.264C903.497,159.332 898.03,157.705 892.833,158.981L841.544,171.589C838.223,168.654 834.845,165.756 831.43,162.916L833.278,108.002C833.476,102.443 830.885,97.161 826.434,94.077C821.988,90.973 816.341,90.504 811.478,92.811L763.631,115.558C759.777,113.348 755.903,111.158 751.987,109.041L743.532,54.926C742.675,49.444 739.147,44.788 734.206,42.661C729.283,40.523 723.638,41.213 719.315,44.469L676.656,76.476C672.456,75.08 668.237,73.743 663.964,72.465L645.578,21.148C643.708,15.919 639.397,12.077 634.14,10.997C628.901,9.926 623.51,11.74 619.877,15.799L583.97,55.971C579.628,55.471 575.285,55.015 570.927,54.639L543.204,7.926C540.394,3.194 535.434,0.314 530.088,0.314C524.754,0.314 519.784,3.194 516.998,7.926L489.265,54.639C484.907,55.015 480.543,55.471 476.209,55.971L440.299,15.799C436.663,11.74 431.252,9.926 426.031,10.997C420.776,12.089 416.458,15.919 414.598,21.148L396.196,72.465C391.936,73.743 387.715,75.092 383.505,76.476L340.861,44.469C336.525,41.203 330.881,40.514 325.945,42.661C321.026,44.788 317.484,49.444 316.632,54.926L308.171,109.041C304.257,111.158 300.382,113.335 296.518,115.558L248.676,92.811C243.818,90.496 238.147,90.973 233.722,94.077C229.277,97.161 226.68,102.443 226.882,108.002L228.717,162.916C225.312,165.756 221.943,168.654 218.605,171.589L167.326,158.981C162.115,157.716 156.656,159.332 152.885,163.264C149.09,167.21 147.553,172.895 148.772,178.328L160.851,231.818C158.049,235.285 155.276,238.789 152.558,242.339L99.903,240.426C94.588,240.269 89.516,242.923 86.547,247.559C83.572,252.172 83.122,258.08 85.336,263.167L107.15,313.07C105.031,317.078 102.926,321.118 100.901,325.22L49.018,334.025C43.747,334.913 39.304,338.591 37.254,343.735C35.217,348.892 35.878,354.768 38.989,359.29L69.679,403.741C69.442,404.525 69.224,405.317 68.989,406.105L52.126,424.017L97.467,488.066C97.467,488.066 532.619,688.798 936.264,491.462C982.372,483.189 993.119,412.903 993.119,412.903Z" style="fill:rgb(228,58,37);fill-rule:nonzero;"/>
<path d="M608.303,376.759C608.303,376.759 656.46,324.03 704.618,376.759C704.618,376.759 742.458,447.071 704.618,482.222C704.618,482.222 642.701,531.439 608.303,482.222C608.303,482.222 567.024,443.55 608.303,376.759Z" style="fill:rgb(3,4,4);fill-rule:nonzero;"/>
<path d="M664.057,396.32C664.057,416.853 651.954,433.499 637.027,433.499C622.103,433.499 610,416.853 610,396.32C610,375.788 622.103,359.14 637.027,359.14C651.954,359.14 664.057,375.788 664.057,396.32Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M393.365,362.361C393.365,362.361 475.973,325.785 498.519,407.423C498.519,407.423 522.137,502.577 430.682,507.948C430.682,507.948 314.06,485.486 393.365,362.361Z" style="fill:rgb(3,4,4);fill-rule:nonzero;"/>
<path d="M434.855,397.668C434.855,418.841 422.375,436.014 406.978,436.014C391.587,436.014 379.104,418.841 379.104,397.668C379.104,376.49 391.587,359.322 406.978,359.322C422.375,359.322 434.855,376.49 434.855,397.668Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M111.602,499.216C122.569,486.753 149.213,471.659 147.172,452.934C143.519,419.407 115.716,394.935 85.073,398.275C77.473,399.103 70.415,401.567 64.149,405.311C63.687,405.204 63.224,405.1 62.761,405.017C62.761,405.017 -40.87,455.89 18.197,557.674C18.754,562.811 136.045,713.342 168.985,724.805C190.544,732.307 149.074,596.165 111.602,499.216Z" style="fill:rgb(228,58,37);fill-rule:nonzero;"/>
<path d="M953.549,494.673C940.856,483.973 907.387,474.255 906.629,455.435C905.273,421.737 929.141,393.414 959.941,392.175C967.579,391.867 974.925,393.258 981.676,396.032C982.118,395.858 982.56,395.686 983.005,395.535C983.005,395.535 1093.03,430.486 1049.7,539.901C1049.91,545.064 956.232,711.317 925.355,727.536C905.146,738.151 930.861,596.105 953.549,494.673Z" style="fill:rgb(228,58,37);fill-rule:nonzero;"/>
<path d="M191.142,495.558C191.142,495.558 189.759,632.854 324.308,663.49L352.362,607.127C352.362,607.127 254.867,616.558 247.367,495.558L191.142,495.558Z" style="fill:rgb(228,58,37);fill-rule:nonzero;"/>
<path d="M876.362,495.558C876.362,495.558 877.744,632.854 743.195,663.49L715.141,607.127C715.141,607.127 812.636,616.558 820.136,495.558L876.362,495.558Z" style="fill:rgb(228,58,37);fill-rule:nonzero;"/>
<path d="M779.167,635.591C758.917,586.649 693.572,567.218 633.216,592.191C580.09,614.172 548.579,663.223 555.592,708.036C597.538,707.384 642.532,704.665 686.328,698.318C686.328,698.318 660.491,740.081 622.471,776.529C648.037,783.128 677.854,781.297 706.547,769.425C766.904,744.452 799.417,684.532 779.167,635.591Z" style="fill:rgb(228,58,37);fill-rule:nonzero;"/>
<path d="M404.746,695.984C404.746,695.984 459.949,703.279 535.416,705.14C542.026,657.629 506.036,607.348 448.615,587.897C385.177,566.409 319.626,590.689 302.201,642.129C284.776,693.569 322.077,752.689 385.515,774.178C413.636,783.704 442.168,784.227 466.744,777.385C429.833,740.88 404.746,695.984 404.746,695.984Z" style="fill:rgb(228,58,37);fill-rule:nonzero;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="1200" height="800" viewBox="0 0 1200 800" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<g id="Layer-1" serif:id="Layer 1">
<g transform="matrix(1,0,0,1,1009.4,506.362)">
<path d="M0,-7.203L-12.072,-32.209C-12.009,-33.156 -11.961,-34.107 -11.961,-35.062C-11.961,-63.408 -41.439,-89.533 -91.03,-110.451L-91.03,-93.058C-95.866,-94.977 -100.901,-96.845 -106.147,-98.651L-106.147,-106.759C-177.021,-132.319 -282.53,-148.537 -400.388,-148.537C-503.361,-148.537 -596.917,-136.157 -666.179,-115.983L-666.179,-87.737L-666.181,-87.737L-666.181,-121.925C-737.141,-99.375 -781.135,-68.048 -781.135,-33.41C-781.135,-27.95 -780.034,-22.572 -777.918,-17.297L-785.146,-4.43C-785.146,-4.43 -790.938,3.082 -780.74,18.932C-771.746,32.909 -726.692,87.617 -702.913,116.267C-692.699,130.954 -685.772,140.001 -685.167,139.126C-684.212,137.74 -691.518,110.165 -711.802,78.703C-721.268,61.808 -732.57,39.42 -739.356,22.884C-720.414,34.874 -609.126,90.913 -382.124,90.685C-150.13,90.453 -47.009,17.834 -35.691,7.948C-39.646,23.837 -53.159,55.981 -63.936,78.586C-81.642,110.917 -88.056,139.064 -87.232,140.456C-86.708,141.334 -80.667,132.015 -71.756,116.913C-51.025,87.37 -11.739,30.974 -3.889,16.608C5.007,0.323 0,-7.203 0,-7.203" style="fill:rgb(165,43,0);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,1079.49,294.885)">
<path d="M0,204.135L-79.343,145.689C-80.088,143.089 -80.833,140.488 -81.603,137.908L-55.541,100.154C-52.881,96.314 -52.345,91.322 -54.072,86.943C-55.803,82.585 -59.587,79.461 -64.062,78.696L-108.128,71.217C-109.837,67.732 -111.626,64.301 -113.422,60.898L-94.907,18.51C-93.004,14.193 -93.402,9.175 -95.929,5.256C-98.446,1.319 -102.715,-0.981 -107.267,-0.802L-151.991,0.823C-154.306,-2.193 -156.658,-5.18 -159.058,-8.114L-148.78,-53.546C-147.738,-58.158 -149.054,-62.989 -152.267,-66.34C-155.462,-69.679 -160.105,-71.062 -164.52,-69.979L-208.082,-59.27C-210.902,-61.763 -213.77,-64.223 -216.67,-66.635L-215.103,-113.276C-214.935,-117.997 -217.136,-122.484 -220.915,-125.105C-224.692,-127.741 -229.485,-128.137 -233.616,-126.179L-274.254,-106.858C-277.527,-108.736 -280.819,-110.595 -284.146,-112.395L-291.327,-158.356C-292.056,-163.012 -295.051,-166.968 -299.246,-168.774C-303.431,-170.591 -308.222,-170.002 -311.894,-167.238L-348.126,-140.053C-351.695,-141.238 -355.279,-142.373 -358.905,-143.46L-374.522,-187.045C-376.11,-191.488 -379.772,-194.751 -384.238,-195.669C-388.688,-196.578 -393.266,-195.037 -396.352,-191.589L-426.851,-157.47C-430.536,-157.893 -434.228,-158.28 -437.927,-158.601L-461.476,-198.277C-463.86,-202.295 -468.073,-204.741 -472.615,-204.741C-477.144,-204.741 -481.365,-202.295 -483.733,-198.277L-507.288,-158.601C-510.989,-158.28 -514.696,-157.893 -518.376,-157.47L-548.875,-191.589C-551.965,-195.037 -556.559,-196.578 -560.997,-195.669C-565.457,-194.739 -569.125,-191.488 -570.704,-187.045L-586.333,-143.46C-589.954,-142.373 -593.538,-141.23 -597.113,-140.053L-633.333,-167.238C-637.016,-170.012 -641.811,-170.599 -646.001,-168.774C-650.182,-166.968 -653.189,-163.012 -653.914,-158.356L-661.1,-112.395C-664.422,-110.595 -667.714,-108.746 -670.995,-106.858L-711.629,-126.179C-715.756,-128.145 -720.574,-127.741 -724.333,-125.105C-728.106,-122.484 -730.313,-117.997 -730.143,-113.276L-728.581,-66.635C-731.475,-64.223 -734.337,-61.763 -737.172,-59.27L-780.726,-69.979C-785.149,-71.053 -789.788,-69.679 -792.991,-66.34C-796.212,-62.989 -797.517,-58.158 -796.482,-53.546L-786.225,-8.114C-788.603,-5.169 -790.958,-2.193 -793.267,0.823L-837.991,-0.802C-842.504,-0.937 -846.812,1.319 -849.334,5.256C-851.861,9.175 -852.244,14.193 -850.363,18.51L-831.835,60.898C-833.634,64.301 -835.421,67.732 -837.144,71.217L-881.207,78.696C-885.686,79.45 -889.459,82.572 -891.201,86.943C-892.929,91.322 -892.368,96.314 -889.727,100.154L-863.661,137.908C-863.862,138.575 -864.048,139.247 -864.248,139.916L-937.944,218.201C-937.944,218.201 -949.24,227.052 -932.797,247.855C-918.297,266.206 -843.846,338.951 -804.526,377.06C-787.92,396.408 -776.542,408.389 -775.354,407.353C-773.478,405.708 -783.326,370.506 -816.036,329.204C-841.252,292.148 -873.977,235.155 -866.303,228.586C-866.303,228.586 -857.574,217.505 -840.061,209.529C-839.42,210.041 -840.723,209.022 -840.061,209.529C-840.061,209.529 -470.466,380.02 -127.632,212.413C-88.468,205.388 -64.759,226.368 -64.759,226.368C-56.583,231.108 -77.755,289.712 -95.166,328.505C-118.845,372.555 -122.317,406.927 -120.31,408.119C-119.042,408.876 -110.427,395.766 -98.138,374.902C-67.814,332.649 -10.492,252.1 0,232.534C11.895,210.352 0,204.135 0,204.135" style="fill:rgb(247,76,0);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,917.896,244.679)">
<path d="M0,232.466C0,232.466 53.179,230 123.032,159.004L132.93,137.025C132.93,137.025 24.513,29.177 193.048,-45.266C193.048,-45.266 178.293,-21.154 182.622,72.006C182.622,72.006 233.437,54.357 248.336,-27.934C248.336,-27.934 322.456,69.79 167.834,161.443C167.834,161.443 95.294,277.732 -6.971,266.593L0,232.466Z" style="fill:rgb(247,76,0);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,676.997,488.361)">
<path d="M0,-78.192C0,-78.192 36.935,-118.635 73.871,-78.192C73.871,-78.192 102.893,-24.265 73.871,2.695C73.871,2.695 26.384,40.443 0,2.695C0,2.695 -31.658,-26.964 0,-78.192" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,719.761,425.169)">
<path d="M0,0.004C0,15.75 -9.282,28.518 -20.732,28.518C-32.18,28.518 -41.462,15.75 -41.462,0.004C-41.462,-15.746 -32.18,-28.514 -20.732,-28.514C-9.282,-28.514 0,-15.746 0,0.004" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,512.148,482.736)">
<path d="M0,-83.609C0,-83.609 63.355,-111.661 80.648,-49.047C80.648,-49.047 98.762,23.933 28.618,28.052C28.618,28.052 -60.826,10.824 0,-83.609" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,543.968,426.204)">
<path d="M0,0.002C0,16.241 -9.572,29.411 -21.381,29.411C-33.185,29.411 -42.76,16.241 -42.76,0.002C-42.76,-16.242 -33.185,-29.409 -21.381,-29.409C-9.572,-29.409 0,-16.242 0,0.002" style="fill:white;fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,593.317,576.574)">
<path d="M0,-40.271L80.796,-46.755C80.796,-46.755 78.058,-33.749 67.517,-23.986C67.517,-23.986 39.727,6.484 7.844,-26.519C7.844,-26.519 2.627,-32.148 0,-40.271" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(1,0,0,1,269.796,270.778)">
<path d="M0,190.741C-0.667,190.741 -1.321,190.79 -1.973,190.842C-28.207,184.871 -101.946,165.657 -121.437,134.479C-121.437,134.479 -22.21,21.607 -177.297,-50.54L-159.24,74.338C-159.24,74.338 -207.049,42.389 -217.366,-27.008C-217.366,-27.008 -333.789,57.486 -165.982,138.466C-165.982,138.466 -150.762,195.653 -4.633,241.281L-4.526,240.846C-3.055,241.118 -1.549,241.281 0,241.281C13.808,241.281 25.003,229.969 25.003,216.01C25.003,202.054 13.808,190.741 0,190.741" style="fill:rgb(247,76,0);fill-rule:nonzero;"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 247.73 83.02">
<defs>
<style>
.cls-1 {
fill: #f1f2f2;
font-family: SignPainter-HouseScript, SignPainter;
font-size: 62px;
}
.cls-2 {
letter-spacing: .02em;
}
.cls-3 {
letter-spacing: .03em;
}
.cls-4 {
letter-spacing: .02em;
}
.cls-5 {
letter-spacing: .02em;
}
.cls-6 {
letter-spacing: .02em;
}
</style>
</defs>
<g id="Layer_1-2" data-name="Layer 1">
<text class="cls-1" transform="translate(.38 52.7)"><tspan class="cls-5" x="0" y="0">Je</tspan><tspan class="cls-2" x="49.41" y="0">s</tspan><tspan class="cls-4" x="69.69" y="0">ú</tspan><tspan class="cls-2" x="96.04" y="0">s </tspan><tspan class="cls-6" x="131.5" y="0"></tspan><tspan class="cls-3" x="179.55" y="0">rez</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 987 B

View File

@ -0,0 +1,402 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 387.39 354.4">
<defs>
<style>
.cls-1, .cls-2, .cls-3, .cls-4, .cls-5, .cls-6, .cls-7, .cls-8, .cls-9, .cls-10, .cls-11, .cls-12, .cls-13 {
stroke: #231f20;
}
.cls-1, .cls-2, .cls-3, .cls-4, .cls-5, .cls-7, .cls-8, .cls-9, .cls-12, .cls-13 {
stroke-miterlimit: 10;
}
.cls-1, .cls-5, .cls-6 {
fill: none;
}
.cls-2 {
stroke-dasharray: 5.24 4.85;
}
.cls-2, .cls-3, .cls-4, .cls-14, .cls-10, .cls-11 {
opacity: .38;
}
.cls-2, .cls-3, .cls-4, .cls-10, .cls-11, .cls-13 {
fill: #e6e7e8;
}
.cls-3 {
stroke-dasharray: 1.89 3.79;
stroke-width: .99px;
}
.cls-4 {
stroke-dasharray: .84 1.39;
stroke-width: 1.11px;
}
.cls-15, .cls-7, .cls-8 {
fill: #fff;
}
.cls-5 {
stroke-dasharray: .67 1.11;
}
.cls-16 {
opacity: .44;
}
.cls-7, .cls-9 {
stroke-width: .62px;
}
.cls-8 {
stroke-width: .6px;
}
.cls-9 {
fill: #231f20;
}
.cls-10 {
stroke-width: 1.84px;
}
.cls-11 {
stroke-width: 1.84px;
}
.cls-13 {
stroke-width: 2px;
}
/* Animación de rotación con cambio real de dirección */
.yin-yang-rotation {
animation: yinYangDirectionChange 20s linear infinite;
transform-origin: 195.5px 177.2px;
}
.pentagon-inner-rotation {
animation: pentagonDirectionChange 20s linear infinite;
transform-origin: 193.9px 176.2px;
}
/* Yin-Yang: cambios de dirección alternos */
@keyframes yinYangDirectionChange {
0% { transform: rotate(0deg); }
50% { transform: rotate(540deg); }
50.01% { transform: rotate(540deg); }
100% { transform: rotate(0deg); }
}
/* Pentágono: cambios de dirección alternos con retardo */
@keyframes pentagonDirectionChange {
0% { transform: rotate(0deg); }
55% { transform: rotate(-594deg); }
55.01% { transform: rotate(-594deg); }
100% { transform: rotate(0deg); }
}
/* Transformación del fondo yin: negro → blanco */
.yin-background {
animation: yinBackgroundChange 20s ease-in-out infinite;
}
.yang-background {
animation: yangBackgroundChange 20s ease-in-out infinite;
}
/* Bordes dinámicos para yin */
.yin-background {
animation: yinBackgroundChange 20s ease-in-out infinite, yinBorderChange 20s ease-in-out infinite;
}
.yang-background {
animation: yangBackgroundChange 20s ease-in-out infinite, yangBorderChange 20s ease-in-out infinite;
}
@keyframes yinBackgroundChange {
/* Negro inicial */
0%, 47% { fill: #231f20; }
/* Transición ultra rápida por gris (casi imperceptible) */
49.5% { fill: #555; }
50.5% { fill: #aaa; }
/* Cambio a blanco */
53%, 100% { fill: #fff; }
}
@keyframes yangBackgroundChange {
/* Blanco inicial */
0%, 47% { fill: #fff; }
/* Transición ultra rápida por gris (casi imperceptible) */
49.5% { fill: #aaa; }
50.5% { fill: #555; }
/* Cambio a negro */
53%, 100% { fill: #231f20; }
}
@keyframes yinBorderChange {
/* Borde blanco cuando es negro */
0%, 47% {
stroke: #fff;
stroke-width: 0.5;
}
/* Transición */
49.5% {
stroke: #aaa;
stroke-width: 0.5;
}
50.5% {
stroke: #666;
stroke-width: 0.5;
}
/* Borde negro cuando es blanco */
53%, 100% {
stroke: #231f20;
stroke-width: 0.5;
}
}
@keyframes yangBorderChange {
/* Borde negro cuando es blanco */
0%, 47% {
stroke: #231f20;
stroke-width: 0.5;
}
/* Transición */
49.5% {
stroke: #666;
stroke-width: 0.5;
}
50.5% {
stroke: #aaa;
stroke-width: 0.5;
}
/* Borde blanco cuando es negro */
53%, 100% {
stroke: #fff;
stroke-width: 0.5;
}
}
/* Transformación de los círculos interiores con expansión */
.inner-circle-yin {
animation: innerYinExpand 20s ease-in-out infinite;
transform-origin: 138.77px 47.52px;
}
.inner-circle-yang {
animation: innerYangExpand 20s ease-in-out infinite;
transform-origin: 251.07px 305.08px;
}
@keyframes innerYinExpand {
/* Punto negro pequeño */
0%, 35% {
fill: #231f20;
r: 10;
}
/* Expansión del punto negro */
45% {
fill: #231f20;
r: 50;
}
50% {
fill: #231f20;
r: 80;
}
/* Contracción como punto blanco */
55% {
fill: #fff;
r: 50;
}
65%, 100% {
fill: #fff;
r: 10;
}
}
@keyframes innerYangExpand {
/* Punto blanco pequeño */
0%, 35% {
fill: #fff;
r: 10;
}
/* Expansión del punto blanco */
45% {
fill: #fff;
r: 50;
}
50% {
fill: #fff;
r: 80;
}
/* Contracción como punto negro */
55% {
fill: #231f20;
r: 50;
}
65%, 100% {
fill: #231f20;
r: 10;
}
}
/* Círculo central del pentágono que cambia con la dirección */
.pentagon-center-circle {
animation: pentagonCenterChange 20s ease-in-out infinite;
}
@keyframes pentagonCenterChange {
/* Blanco con borde negro */
0%, 50% {
fill: #fff;
stroke: #231f20;
stroke-width: 1;
}
/* Transición ultra rápida */
54.5% {
fill: #ddd;
stroke: #231f20;
stroke-width: 1;
}
55.5% {
fill: #bbb;
stroke: #231f20;
stroke-width: 1;
}
/* Gris claro con borde negro */
60%, 100% {
fill: #999;
stroke: #231f20;
stroke-width: 1;
}
}
/* Puntos de fondo que cambian de gris fuerte a blanco */
.background-dots {
animation: backgroundDotsChange 20s ease-in-out infinite;
}
@keyframes backgroundDotsChange {
/* Gris fuerte inicial */
0%, 47% {
fill: #555;
}
/* Transición ultra rápida */
49.5% {
fill: #888;
}
50.5% {
fill: #bbb;
}
/* Blanco */
53%, 100% {
fill: #fff;
}
}
</style>
</defs>
<!-- Elementos estáticos de fondo -->
<g>
<path class="cls-10" d="M275.04,224.67c-26.21,45.05-83.99,60.32-129.05,34.11-45.07-26.22-60.34-83.99-34.13-129.05,26.22-45.06,84-60.34,129.06-34.12,45.06,26.21,60.34,83.99,34.12,129.06Z"/>
<path class="cls-10" d="M227.57,306.26c-26.21,45.06-83.99,60.33-129.05,34.12-45.07-26.21-60.34-84-34.13-129.05,26.21-45.06,84-60.34,129.06-34.12,45.06,26.21,60.33,83.99,34.12,129.05Z"/>
<path class="cls-11" d="M322.49,143.11c-26.21,45.05-84,60.33-129.06,34.12-45.06-26.22-60.34-84-34.13-129.06C185.52,3.11,243.3-12.16,288.37,14.06c45.06,26.21,60.34,83.99,34.12,129.06Z"/>
<path class="cls-10" d="M369.4,224.99c-26.21,45.06-84,60.34-129.05,34.12-45.07-26.22-60.34-84-34.13-129.05,26.22-45.07,83.99-60.34,129.06-34.12,45.05,26.21,60.34,83.99,34.12,129.05Z"/>
<path class="cls-10" d="M321.95,306.55c-26.22,45.06-84,60.33-129.05,34.12-45.07-26.22-60.34-83.99-34.13-129.05,26.22-45.06,83.99-60.34,129.06-34.12,45.05,26.21,60.34,83.99,34.12,129.05Z"/>
<path class="cls-10" d="M180.65,224.39c-26.21,45.05-84,60.34-129.05,34.13C6.53,232.29-8.74,174.51,17.48,129.45c26.21-45.06,83.99-60.34,129.05-34.12,45.05,26.21,60.34,83.99,34.12,129.05Z"/>
<path class="cls-10" d="M228.12,142.79c-26.22,45.06-84,60.34-129.06,34.12-45.07-26.22-60.34-84-34.12-129.05C91.16,2.8,148.93-12.48,193.99,13.74c45.06,26.21,60.34,83.99,34.13,129.05Z"/>
<line class="cls-4" x1="51.92" y1="258.37" x2="335.17" y2="96.19"/>
<line class="cls-4" x1="51.92" y1="95.15" x2="334.43" y2="259.11"/>
<line class="cls-2" x1="52.22" y1="258.96" x2="192.95" y2="340.57"/>
<line class="cls-2" x1="193.1" y1="340.57" x2="334.43" y2="259.41"/>
<line class="cls-2" x1="51.63" y1="258.07" x2="51.92" y2="95.15"/>
<line class="cls-2" x1="52.22" y1="95" x2="193.7" y2="13.84"/>
<line class="cls-2" x1="194.03" y1="13.73" x2="335.32" y2="96.19"/>
<polygon class="cls-3" points="99.04 340.46 4.72 177.1 99.04 13.73 287.69 13.73 382 177.1 287.69 340.46 99.04 340.46"/>
<line class="cls-3" x1="4.74" y1="177.1" x2="382.15" y2="177.1"/>
<line class="cls-3" x1="98.98" y1="13.76" x2="287.6" y2="340.63"/>
<line class="cls-3" x1="99.06" y1="340.46" x2="287.67" y2="13.73"/>
<line class="cls-4" x1="193.99" y1="13.76" x2="192.95" y2="340.65"/>
<polygon class="cls-3" points="146.22 258.65 99.07 177 146.22 95.34 240.51 95.34 287.65 177 240.51 258.65 146.22 258.65"/>
<g class="cls-14">
<path class="cls-13 background-dots" d="M150.14,97.38c-1.2,2.06-3.84,2.76-5.89,1.56-2.06-1.2-2.76-3.83-1.56-5.89,1.2-2.06,3.83-2.76,5.89-1.55,2.06,1.19,2.76,3.83,1.56,5.89Z"/>
<path class="cls-13 background-dots" d="M102.72,179.09c-1.19,2.06-3.83,2.76-5.89,1.56-2.06-1.2-2.75-3.84-1.56-5.89,1.19-2.06,3.83-2.75,5.89-1.56,2.06,1.2,2.76,3.83,1.56,5.89Z"/>
<path class="cls-13 background-dots" d="M9.07,179.3c-1.16,2.08-3.78,2.83-5.86,1.67-2.08-1.15-2.83-3.78-1.67-5.86,1.16-2.08,3.78-2.83,5.86-1.67,2.08,1.16,2.83,3.78,1.67,5.86Z"/>
<path class="cls-13 background-dots" d="M55.84,97.09c-1.16,2.08-3.78,2.83-5.86,1.67-2.08-1.16-2.83-3.78-1.67-5.86,1.16-2.08,3.78-2.83,5.86-1.67,2.08,1.15,2.82,3.78,1.67,5.86Z"/>
<path class="cls-13 background-dots" d="M244.64,97.84c-1.2,2.06-3.83,2.75-5.89,1.56-2.06-1.2-2.76-3.84-1.56-5.89,1.2-2.05,3.84-2.75,5.89-1.56,2.06,1.2,2.75,3.83,1.56,5.89Z"/>
<path class="cls-13 background-dots" d="M339.09,98.29c-1.16,2.08-3.78,2.83-5.86,1.67-2.08-1.15-2.83-3.78-1.67-5.86,1.15-2.08,3.78-2.83,5.86-1.67,2.08,1.15,2.83,3.78,1.67,5.86Z"/>
<path class="cls-13 background-dots" d="M385.85,179.3c-1.16,2.08-3.78,2.83-5.86,1.67-2.08-1.16-2.83-3.78-1.67-5.86,1.15-2.08,3.78-2.83,5.86-1.67,2.08,1.15,2.83,3.78,1.67,5.86Z"/>
<path class="cls-13 background-dots" d="M197.75,15.76c-1.16,2.08-3.78,2.83-5.86,1.67-2.08-1.16-2.83-3.78-1.67-5.86,1.15-2.08,3.78-2.82,5.86-1.67,2.08,1.16,2.83,3.78,1.67,5.86Z"/>
<path class="cls-13 background-dots" d="M291.58,15.93c-1.16,2.08-3.78,2.82-5.86,1.67-2.08-1.15-2.83-3.78-1.67-5.86,1.15-2.08,3.78-2.82,5.86-1.66,2.08,1.15,2.83,3.78,1.67,5.86Z"/>
<path class="cls-13 background-dots" d="M102.6,15.68c-1.16,2.08-3.78,2.82-5.86,1.67-2.08-1.15-2.83-3.78-1.67-5.86,1.15-2.08,3.78-2.82,5.86-1.67,2.08,1.15,2.83,3.78,1.67,5.86Z"/>
<path class="cls-13 background-dots" d="M236.75,256.72c1.18-2.06,3.82-2.78,5.88-1.59,2.06,1.19,2.77,3.82,1.59,5.88-1.19,2.06-3.82,2.77-5.88,1.59-2.06-1.19-2.77-3.83-1.58-5.88Z"/>
<path class="cls-13 background-dots" d="M189.03,338.35c1.14-2.09,3.77-2.85,5.85-1.7,2.08,1.14,2.84,3.77,1.7,5.85-1.14,2.08-3.76,2.84-5.85,1.7-2.09-1.15-2.85-3.77-1.7-5.85Z"/>
<path class="cls-13 background-dots" d="M95.06,338.42c1.14-2.09,3.77-2.85,5.85-1.71,2.08,1.15,2.85,3.77,1.7,5.86-1.14,2.08-3.76,2.84-5.85,1.7-2.09-1.15-2.85-3.77-1.7-5.85Z"/>
<path class="cls-13 background-dots" d="M283.6,338.42c1.14-2.09,3.77-2.85,5.85-1.71,2.09,1.15,2.85,3.77,1.7,5.86-1.14,2.08-3.77,2.84-5.85,1.7-2.09-1.15-2.85-3.77-1.7-5.85Z"/>
<path class="cls-13 background-dots" d="M283.91,175.24c1.19-2.06,3.82-2.77,5.89-1.59,2.05,1.19,2.76,3.83,1.58,5.88-1.18,2.07-3.82,2.78-5.88,1.6-2.06-1.19-2.78-3.82-1.59-5.89Z"/>
<path class="cls-13 background-dots" d="M142.24,256.71c1.19-2.07,3.82-2.77,5.89-1.59,2.06,1.2,2.77,3.83,1.58,5.89-1.18,2.06-3.82,2.77-5.88,1.58-2.06-1.18-2.77-3.82-1.58-5.88Z"/>
<path class="cls-13 background-dots" d="M48,256.29c1.15-2.09,3.76-2.85,5.85-1.7,2.08,1.15,2.84,3.77,1.7,5.85-1.14,2.08-3.77,2.84-5.85,1.69-2.08-1.14-2.84-3.77-1.69-5.85Z"/>
<path class="cls-13 background-dots" d="M330.7,257.21c1.18-2.06,3.82-2.78,5.88-1.59,2.06,1.18,2.77,3.82,1.58,5.88-1.18,2.06-3.82,2.77-5.88,1.59-2.06-1.19-2.77-3.82-1.58-5.88Z"/>
</g>
</g>
<!-- Símbolo Yin-Yang con rotación y cambio de dirección real -->
<g class="yin-yang-rotation">
<g>
<!-- Formas de fondo que cambian de negro a blanco y viceversa -->
<path class="yin-background" d="M337.95,256.21c-.69-23.4-9.19-45.58-25.33-62.76-11.71-12.47-28.78-22.47-45.42-26.33-.08,0-.16,0-.23-.02-1.28-.24-2.55-.51-3.81-.84-.04,0-.07.01-.11.01-.11,0-.2-.02-.29-.05-.29,0-.58-.02-.86-.09-.07,0-.14,0-.21,0-.03,0-.06,0-.09-.01-.04,0-.07.02-.11.02-.09,0-.17-.01-.24-.03l-.91.13-.12,2.03c.91.26,1.1.43,1.37.56,2.69,1.23,6.13,2.56,8.62,4.13.14.09,2.75,1.49,3.15,1.8,3.18,2.4,3.57,3.83,6.18,7.16,4.73,6.04,8.22,13.27,10.03,20.82.27,1.11.65,8.07.58,9.44-.73,13.98-8.36,28.85-19.7,36.6-5.67,3.87-12.26,5.46-18.88,7.26,0,0-3.33.71-4.76.9-1.49.2-3.42.63-5.16.89-3.19.47-8.5,2.85-11.43,4.34-7.88,4-15.25,10.89-19.47,18.6-2.34,4.28-4.59,10.04-5.25,14.91-.07.52-.28,2.12-.3,2.58-.7,11.39,1.94,23.72,8.25,32.68,2.38,3.38,7.91,8.82,11.56,11.22,3.8,2.5,9.88,5.34,14.35,6.61.96.27,2.02.14,2.79.33.24.06.08.52.61.59,9.41,1.23,23.13-1.24,31.32-3.79,3.85-1.2,9-3.35,12.69-5.16,31.06-15.22,52.19-49.81,51.17-84.5ZM255.64,305.03c.27.28.38.74.15,1.09l-1.06,1.6c-.17.25-.42.4-.69.43-.11.11-.22.2-.38.25-.03,0-.06.01-.09.02l-.59,1.1c-.57.23-1.19.34-1.8.24-.15-.03-.31-.08-.45-.15-1.43-.17-2.83-1.16-3.57-2.32-2.09-3.28,2.13-8.31,4.93-7.08.02,0,.03.03.04.06.02,0,.05,0,.07,0,.47.11.9.33,1.28.63.21.04.41.09.61.18.43.2,1.89,2.22,1.86,2.91l-.4.74c.05.1.09.2.1.3Z"/>
<path class="yang-background" d="M52.08,96.22c.69,23.4,9.19,45.58,25.33,62.76,11.71,12.47,28.78,22.47,45.42,26.33.08,0,.16,0,.23.02,1.28.24,2.55.51,3.81.84.04,0,.07-.01.11-.01.11,0,.2.02.29.05.29,0,.58.02.86.09.07,0,.14,0,.21,0,.03,0,.06,0,.09.01.04,0,.07-.02.11-.02.09,0,.17.01.24.03l.91-.13.12-2.03c-.91-.26-1.1-.43-1.37-.56-2.69-1.23-6.13-2.56-8.62-4.13-.14-.09-2.75-1.49-3.15-1.8-3.18-2.4-3.57-3.83-6.18-7.16-4.73-6.04-8.22-13.27-10.03-20.82-.27-1.11-.65-8.07-.58-9.44.73-13.98,8.36-28.85,19.7-36.6,5.67-3.87,12.26-5.46,18.88-7.26,0,0,3.33-.71,4.76-.9,1.49-.2,3.42-.63,5.16-.89,3.19-.47,8.5-2.85,11.43-4.34,7.88-4,15.25-10.89,19.47-18.6,2.34-4.28,4.59-10.04,5.25-14.91.07-.52.28-2.12.3-2.58.7-11.39-1.94-23.72-8.25-32.68-2.38-3.38-7.91-8.82-11.56-11.22-3.8-2.5-9.88-5.34-14.35-6.61-.96-.27-2.02-.14-2.79-.33-.24-.06-.08-.52-.61-.59-9.41-1.23-23.13,1.24-31.32,3.79-3.85,1.2-9,3.35-12.69,5.16-31.06,15.22-52.19,49.81-51.17,84.5ZM134.39,47.4c-.27-.28-.38-.74-.15-1.09l1.06-1.6c.17-.25.42-.4.69-.43.11-.11.22-.2.38-.25.03,0,.06-.01.09-.02l.59-1.1c.57-.23,1.19-.34,1.8-.24.15.03.31.08.45.15,1.43.17,2.83,1.16,3.57,2.32,2.09,3.28-2.13,8.31-4.93,7.08-.02,0-.03-.03-.04-.06-.02,0-.05,0-.07,0-.47-.11-.9-.33-1.28-.63-.21-.04-.41-.09-.61-.18-.43-.2-1.89-2.22-1.86-2.91l.4-.74c-.05-.1-.09-.2-.1-.3Z"/>
<!-- Círculos interiores con expansión transformadora -->
<circle class="inner-circle-yin" cx="138.77" cy="47.52" r="10"/>
<circle class="inner-circle-yang" cx="251.07" cy="305.08" r="10"/>
</g>
</g>
<!-- Parte interior del pentágono con rotación y cambio de dirección -->
<g class="pentagon-inner-rotation">
<g>
<path class="cls-15" d="M137.39,243.49l-30.83-94.89,80.72-58.64c3.94-2.87,9.29-2.87,13.23,0l80.72,58.64-30.83,94.89c-1.51,4.64-5.83,7.78-10.7,7.78h-91.59c-4.88,0-9.2-3.14-10.7-7.78Z"/>
<g>
<path class="cls-15" d="M195.67,85.2c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-15" d="M282.88,148.58c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-15" d="M249.56,251.2c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-15" d="M141.63,251.24c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-15" d="M108.25,148.6c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
</g>
<!-- Círculo central que cambia de blanco a gris con el sentido de rotación -->
<circle class="pentagon-center-circle" cx="193.9" cy="176.2" r="5" fill="#fff" stroke="#231f20" stroke-width="1"/>
</g>
<g>
<g class="cls-16">
<path class="cls-6" d="M137.1,243.18l-30.83-94.89,80.72-58.64c3.94-2.87,9.29-2.87,13.23,0l80.72,58.64-30.83,94.89c-1.51,4.64-5.83,7.78-10.7,7.78h-91.59c-4.88,0-9.2-3.14-10.7-7.78Z"/>
<path class="cls-6" d="M187.35,238.06l-49.79-35.92c-3.95-2.85-5.62-7.93-4.13-12.57l18.78-58.46c1.49-4.64,5.8-7.8,10.68-7.81l61.41-.2c4.87-.02,9.19,3.1,10.72,7.72l19.25,58.24c1.53,4.64-.1,9.73-4.04,12.61l-49.66,36.34c-3.93,2.88-9.28,2.9-13.23.04Z"/>
<path class="cls-6" d="M212.5,216.61l-40.64-1.6c-4.89-.19-9.09-3.52-10.4-8.24l-10.86-39.18c-1.3-4.69.56-9.68,4.61-12.38l33.82-22.49c4.05-2.7,9.38-2.48,13.2.54l31.92,25.19c3.84,3.03,5.28,8.2,3.57,12.78l-14.24,38.07c-1.7,4.55-6.13,7.49-10.98,7.3Z"/>
<path class="cls-6" d="M219.55,194.36l-21.57,13.19c-4.16,2.54-9.48,2.12-13.19-1.05l-19.21-16.44c-3.7-3.17-4.95-8.36-3.08-12.87l9.7-23.35c1.87-4.5,6.43-7.29,11.29-6.9l25.21,2c4.86.39,8.92,3.86,10.05,8.6l5.88,24.59c1.13,4.74-.91,9.67-5.07,12.22Z"/>
<path class="cls-6" d="M214.73,176.18l-3.69,12.19c-1.41,4.67-5.67,7.89-10.54,7.99l-12.74.26c-4.87.1-9.26-2.95-10.86-7.56l-4.18-12.04c-1.6-4.61-.05-9.72,3.84-12.66l10.15-7.69c3.89-2.94,9.23-3.05,13.23-.27l10.45,7.28c4,2.79,5.75,7.83,4.34,12.5Z"/>
<path class="cls-6" d="M207.42,170.16l1.37,4.95c1.3,4.7-.58,9.7-4.64,12.39l-4.28,2.83c-4.07,2.69-9.41,2.45-13.22-.59l-4.02-3.2c-3.81-3.04-5.23-8.19-3.52-12.75l1.8-4.81c1.71-4.56,6.17-7.51,11.04-7.29l5.13.23c4.87.22,9.05,3.55,10.35,8.24Z"/>
<path class="cls-6" d="M202.15,169h0c3.28,3.61,3.87,8.92,1.46,13.15h0c-2.41,4.24-7.28,6.44-12.06,5.45h0c-4.77-.99-8.37-4.94-8.91-9.78h0c-.54-4.85,2.11-9.49,6.55-11.5h0c4.44-2.01,9.67-.93,12.96,2.68Z"/>
<path class="cls-5" d="M164.74,244.33l-44.12-61.11c-2.85-3.95-2.84-9.29.04-13.23l44.49-60.86c2.88-3.94,7.96-5.57,12.59-4.05l71.61,23.5c4.64,1.52,7.77,5.87,7.74,10.75l-.38,75.34c-.02,4.86-3.17,9.16-7.79,10.65l-71.6,23.13c-4.64,1.5-9.73-.16-12.58-4.12Z"/>
<path class="cls-5" d="M201.7,228.64l-47.54-17.41c-4.58-1.68-7.56-6.11-7.38-10.98l1.87-50.58c.18-4.87,3.48-9.07,8.17-10.41l48.71-13.86c4.68-1.33,9.68.49,12.41,4.53l28.33,41.95c2.74,4.05,2.54,9.41-.48,13.25l-31.37,39.91c-3.01,3.83-8.14,5.29-12.72,3.61Z"/>
<path class="cls-5" d="M217.57,205.34l-31.71,7.81c-4.76,1.17-9.74-.87-12.29-5.06l-17-27.81c-2.53-4.15-2.12-9.45,1.02-13.15l21.08-24.82c3.16-3.72,8.35-4.98,12.86-3.13l30.09,12.36c4.51,1.85,7.31,6.4,6.95,11.26l-2.46,32.46c-.37,4.85-3.81,8.91-8.53,10.08Z"/>
<path class="cls-5" d="M218.45,184.2l-11.67,14.58c-3.05,3.81-8.2,5.22-12.76,3.5l-17.47-6.59c-4.56-1.72-7.5-6.18-7.27-11.05l.87-18.65c.23-4.87,3.57-9.04,8.27-10.33l18.01-4.94c4.7-1.29,9.7.6,12.38,4.67l10.26,15.6c2.68,4.07,2.43,9.41-.62,13.22Z"/>
<path class="cls-5" d="M210.95,172.25v8.56c.01,4.88-3.12,9.2-7.76,10.71l-8.13,2.65c-4.64,1.51-9.72-.13-12.59-4.08l-5.04-6.92c-2.87-3.94-2.88-9.28-.01-13.23l5.02-6.93c2.86-3.95,7.94-5.6,12.58-4.1l8.14,2.63c4.64,1.5,7.78,5.82,7.79,10.69Z"/>
<path class="cls-5" d="M204.47,169.3l1.13,2.01c2.39,4.25,1.78,9.56-1.53,13.14l-1.56,1.7c-3.3,3.59-8.54,4.64-12.97,2.61l-2.1-.96c-4.43-2.03-7.05-6.69-6.49-11.53l.27-2.29c.56-4.84,4.18-8.77,8.96-9.73l2.27-.46c4.78-.96,9.64,1.26,12.03,5.51Z"/>
<g>
<path class="cls-1" d="M195.38,84.89c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-1" d="M282.59,148.26c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-1" d="M249.27,250.89c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-1" d="M141.34,250.93c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-1" d="M107.96,148.29c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -0,0 +1,151 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 387.4 354.41">
<defs>
<style>
.cls-1 {
stroke-width: 2px;
}
.cls-1, .cls-2, .cls-3, .cls-4, .cls-5, .cls-6, .cls-7, .cls-8, .cls-9, .cls-10, .cls-11 {
stroke: #231f20;
}
.cls-1, .cls-2, .cls-10 {
fill: #e6e7e8;
}
.cls-1, .cls-4, .cls-5, .cls-6, .cls-7, .cls-9, .cls-10, .cls-11 {
stroke-miterlimit: 10;
}
.cls-2 {
stroke-width: 1.84px;
}
.cls-2, .cls-5, .cls-6, .cls-12, .cls-9, .cls-10 {
opacity: .38;
}
.cls-2, .cls-5, .cls-6, .cls-9, .cls-10 {
isolation: isolate;
}
.cls-3, .cls-13, .cls-11 {
fill: #fff;
}
.cls-4, .cls-5, .cls-6, .cls-7, .cls-8, .cls-9 {
fill: none;
}
.cls-5 {
stroke-dasharray: 5.24 4.85;
}
.cls-6 {
stroke-dasharray: .84 1.39;
stroke-width: 1.11px;
}
.cls-14 {
opacity: .44;
}
.cls-7 {
stroke-dasharray: .67 1.11;
}
.cls-9, .cls-10 {
stroke-dasharray: 1.89 3.79;
stroke-width: .99px;
}
.cls-11 {
stroke-width: .25px;
}
</style>
</defs>
<g id="Layer_1-2" data-name="Layer 1">
<g>
<path class="cls-2" d="M275.05,224.67c-26.21,45.05-83.99,60.32-129.05,34.11-45.07-26.22-60.34-83.99-34.13-129.05,26.22-45.06,84-60.34,129.06-34.12,45.06,26.21,60.34,83.99,34.12,129.06Z"/>
<path class="cls-2" d="M227.58,306.26c-26.21,45.06-83.99,60.33-129.05,34.12-45.07-26.21-60.34-84-34.13-129.05,26.21-45.06,84-60.34,129.06-34.12,45.06,26.21,60.33,83.99,34.12,129.05h0Z"/>
<path class="cls-2" d="M322.5,143.11c-26.21,45.05-84,60.33-129.06,34.12-45.06-26.22-60.34-84-34.13-129.06C185.53,3.11,243.31-12.16,288.38,14.06c45.06,26.21,60.34,83.99,34.12,129.06h0Z"/>
<path class="cls-2" d="M369.41,224.99c-26.21,45.06-84,60.34-129.05,34.12-45.07-26.22-60.34-84-34.13-129.05,26.22-45.07,83.99-60.34,129.06-34.12,45.05,26.21,60.34,83.99,34.12,129.05h0Z"/>
<path class="cls-2" d="M321.96,306.55c-26.22,45.06-84,60.33-129.05,34.12-45.07-26.22-60.34-83.99-34.13-129.05,26.22-45.06,83.99-60.34,129.06-34.12,45.05,26.21,60.34,83.99,34.12,129.05Z"/>
<path class="cls-2" d="M180.66,224.39c-26.21,45.05-84,60.34-129.05,34.13C6.54,232.29-8.73,174.51,17.49,129.45c26.21-45.06,83.99-60.34,129.05-34.12,45.05,26.21,60.34,83.99,34.12,129.05h0Z"/>
<path class="cls-2" d="M228.13,142.79c-26.22,45.06-84,60.34-129.06,34.12-45.07-26.22-60.34-84-34.12-129.05C91.17,2.8,148.94-12.48,194,13.74c45.06,26.21,60.34,83.99,34.13,129.05h0Z"/>
<line class="cls-6" x1="51.93" y1="258.37" x2="335.18" y2="96.19"/>
<line class="cls-6" x1="51.93" y1="95.15" x2="334.44" y2="259.11"/>
<line class="cls-5" x1="52.23" y1="258.96" x2="192.96" y2="340.57"/>
<line class="cls-5" x1="193.11" y1="340.57" x2="334.44" y2="259.41"/>
<line class="cls-5" x1="51.64" y1="258.07" x2="51.93" y2="95.15"/>
<line class="cls-5" x1="52.23" y1="95" x2="193.71" y2="13.84"/>
<line class="cls-5" x1="194.04" y1="13.73" x2="335.33" y2="96.19"/>
<polygon class="cls-10" points="99.05 340.46 4.73 177.1 99.05 13.73 287.7 13.73 382.01 177.1 287.7 340.46 99.05 340.46"/>
<line class="cls-9" x1="4.75" y1="177.1" x2="382.16" y2="177.1"/>
<line class="cls-9" x1="98.99" y1="13.76" x2="287.61" y2="340.63"/>
<line class="cls-9" x1="99.07" y1="340.46" x2="287.68" y2="13.73"/>
<line class="cls-6" x1="194" y1="13.76" x2="192.96" y2="340.65"/>
<polygon class="cls-10" points="146.23 258.65 99.08 177 146.23 95.34 240.52 95.34 287.66 177 240.52 258.65 146.23 258.65"/>
<g class="cls-12">
<path class="cls-1" d="M150.15,97.38c-1.2,2.06-3.84,2.76-5.89,1.56-2.06-1.2-2.76-3.83-1.56-5.89s3.83-2.76,5.89-1.55c2.06,1.19,2.76,3.83,1.56,5.89h0Z"/>
<path class="cls-1" d="M102.73,179.09c-1.19,2.06-3.83,2.76-5.89,1.56s-2.75-3.84-1.56-5.89c1.19-2.06,3.83-2.75,5.89-1.56,2.06,1.2,2.76,3.83,1.56,5.89Z"/>
<path class="cls-1" d="M9.08,179.3c-1.16,2.08-3.78,2.83-5.86,1.67-2.08-1.15-2.83-3.78-1.67-5.86s3.78-2.83,5.86-1.67,2.83,3.78,1.67,5.86Z"/>
<path class="cls-1" d="M55.85,97.09c-1.16,2.08-3.78,2.83-5.86,1.67-2.08-1.16-2.83-3.78-1.67-5.86s3.78-2.83,5.86-1.67c2.08,1.15,2.82,3.78,1.67,5.86Z"/>
<path class="cls-1" d="M244.65,97.84c-1.2,2.06-3.83,2.75-5.89,1.56-2.06-1.2-2.76-3.84-1.56-5.89s3.84-2.75,5.89-1.56c2.06,1.2,2.75,3.83,1.56,5.89Z"/>
<path class="cls-1" d="M339.1,98.29c-1.16,2.08-3.78,2.83-5.86,1.67-2.08-1.15-2.83-3.78-1.67-5.86,1.15-2.08,3.78-2.83,5.86-1.67,2.08,1.15,2.83,3.78,1.67,5.86Z"/>
<path class="cls-1" d="M385.86,179.3c-1.16,2.08-3.78,2.83-5.86,1.67s-2.83-3.78-1.67-5.86c1.15-2.08,3.78-2.83,5.86-1.67,2.08,1.15,2.83,3.78,1.67,5.86Z"/>
<path class="cls-1" d="M197.76,15.76c-1.16,2.08-3.78,2.83-5.86,1.67s-2.83-3.78-1.67-5.86c1.15-2.08,3.78-2.82,5.86-1.67,2.08,1.16,2.83,3.78,1.67,5.86Z"/>
<path class="cls-1" d="M291.59,15.93c-1.16,2.08-3.78,2.82-5.86,1.67s-2.83-3.78-1.67-5.86c1.15-2.08,3.78-2.82,5.86-1.66,2.08,1.15,2.83,3.78,1.67,5.86h0Z"/>
<path class="cls-1" d="M102.61,15.68c-1.16,2.08-3.78,2.82-5.86,1.67s-2.83-3.78-1.67-5.86c1.15-2.08,3.78-2.82,5.86-1.67s2.83,3.78,1.67,5.86Z"/>
<path class="cls-1" d="M236.76,256.72c1.18-2.06,3.82-2.78,5.88-1.59s2.77,3.82,1.59,5.88c-1.19,2.06-3.82,2.77-5.88,1.59-2.06-1.19-2.77-3.83-1.58-5.88h0Z"/>
<path class="cls-1" d="M189.04,338.35c1.14-2.09,3.77-2.85,5.85-1.7,2.08,1.14,2.84,3.77,1.7,5.85-1.14,2.08-3.76,2.84-5.85,1.7-2.09-1.15-2.85-3.77-1.7-5.85Z"/>
<path class="cls-1" d="M95.07,338.42c1.14-2.09,3.77-2.85,5.85-1.71,2.08,1.15,2.85,3.77,1.7,5.86-1.14,2.08-3.76,2.84-5.85,1.7-2.09-1.15-2.85-3.77-1.7-5.85Z"/>
<path class="cls-1" d="M283.61,338.42c1.14-2.09,3.77-2.85,5.85-1.71,2.09,1.15,2.85,3.77,1.7,5.86-1.14,2.08-3.77,2.84-5.85,1.7-2.09-1.15-2.85-3.77-1.7-5.85Z"/>
<path class="cls-1" d="M283.92,175.24c1.19-2.06,3.82-2.77,5.89-1.59,2.05,1.19,2.76,3.83,1.58,5.88-1.18,2.07-3.82,2.78-5.88,1.6-2.06-1.19-2.78-3.82-1.59-5.89h0Z"/>
<path class="cls-1" d="M142.25,256.71c1.19-2.07,3.82-2.77,5.89-1.59,2.06,1.2,2.77,3.83,1.58,5.89-1.18,2.06-3.82,2.77-5.88,1.58-2.06-1.18-2.77-3.82-1.58-5.88h0Z"/>
<path class="cls-1" d="M48.01,256.29c1.15-2.09,3.76-2.85,5.85-1.7,2.08,1.15,2.84,3.77,1.7,5.85s-3.77,2.84-5.85,1.69c-2.08-1.14-2.84-3.77-1.69-5.85h-.01Z"/>
<path class="cls-1" d="M330.71,257.21c1.18-2.06,3.82-2.78,5.88-1.59,2.06,1.18,2.77,3.82,1.58,5.88-1.18,2.06-3.82,2.77-5.88,1.59-2.06-1.19-2.77-3.82-1.58-5.88Z"/>
</g>
</g>
<path d="M337.96,256.21c-.69-23.4-9.19-45.58-25.33-62.76-11.71-12.47-28.78-22.47-45.42-26.33-.08,0-.16,0-.23-.02-1.28-.24-2.55-.51-3.81-.84-.04,0-.07,0-.11,0-.11,0-.2-.02-.29-.05-.29,0-.58-.02-.86-.09h-.21s-.06,0-.09,0c-.04,0-.07.02-.11.02-.09,0-.17,0-.24-.03l-.91.13-.12,2.03c.91.26,1.1.43,1.37.56,2.69,1.23,6.13,2.56,8.62,4.13.14.09,2.75,1.49,3.15,1.8,3.18,2.4,3.57,3.83,6.18,7.16,4.73,6.04,8.22,13.27,10.03,20.82.27,1.11.65,8.07.58,9.44-.73,13.98-8.36,28.85-19.7,36.6-5.67,3.87-12.26,5.46-18.88,7.26,0,0-3.33.71-4.76.9-1.49.2-3.42.63-5.16.89-3.19.47-8.5,2.85-11.43,4.34-7.88,4-15.25,10.89-19.47,18.6-2.34,4.28-4.59,10.04-5.25,14.91-.07.52-.28,2.12-.3,2.58-.7,11.39,1.94,23.72,8.25,32.68,2.38,3.38,7.91,8.82,11.56,11.22,3.8,2.5,9.88,5.34,14.35,6.61.96.27,2.02.14,2.79.33.24.06.08.52.61.59,9.41,1.23,23.13-1.24,31.32-3.79,3.85-1.2,9-3.35,12.69-5.16,31.06-15.22,52.19-49.81,51.17-84.5v-.03ZM255.65,305.03c.27.28.38.74.15,1.09l-1.06,1.6c-.17.25-.42.4-.69.43-.11.11-.22.2-.38.25-.03,0-.06.01-.09.02l-.59,1.1c-.57.23-1.19.34-1.8.24-.15-.03-.31-.08-.45-.15-1.43-.17-2.83-1.16-3.57-2.32-2.09-3.28,2.13-8.31,4.93-7.08.02,0,.03.03.04.06h.07c.47.11.9.33,1.28.63.21.04.41.09.61.18.43.2,1.89,2.22,1.86,2.91l-.4.74c.05.1.09.2.1.3h0Z"/>
<path class="cls-11" d="M51.09,96.22c.69,23.4,9.19,45.58,25.33,62.76,11.71,12.47,28.78,22.47,45.42,26.33.08,0,.16,0,.23.02,1.28.24,2.55.51,3.81.84.04,0,.07,0,.11,0,.11,0,.2.02.29.05.29,0,.58.02.86.09h.21s.06,0,.09,0c.04,0,.07-.02.11-.02.09,0,.17,0,.24.03l.91-.13.12-2.03c-.91-.26-1.1-.43-1.37-.56-2.69-1.23-6.13-2.56-8.62-4.13-.14-.09-2.75-1.49-3.15-1.8-3.18-2.4-3.57-3.83-6.18-7.16-4.73-6.04-8.22-13.27-10.03-20.82-.27-1.11-.65-8.07-.58-9.44.73-13.98,8.36-28.85,19.7-36.6,5.67-3.87,12.26-5.46,18.88-7.26,0,0,3.33-.71,4.76-.9,1.49-.2,3.42-.63,5.16-.89,3.19-.47,8.5-2.85,11.43-4.34,7.88-4,15.25-10.89,19.47-18.6,2.34-4.28,4.59-10.04,5.25-14.91.07-.52.28-2.12.3-2.58.7-11.39-1.94-23.72-8.25-32.68-2.38-3.38-7.91-8.82-11.56-11.22-3.8-2.5-9.88-5.34-14.35-6.61-.96-.27-2.02-.14-2.79-.33-.24-.06-.08-.52-.61-.59-9.41-1.23-23.13,1.24-31.32,3.79-3.85,1.2-9,3.35-12.69,5.16-31.06,15.22-52.19,49.81-51.17,84.5v.03ZM133.4,47.4c-.27-.28-.38-.74-.15-1.09l1.06-1.6c.17-.25.42-.4.69-.43.11-.11.22-.2.38-.25.03,0,.06,0,.09-.02l.59-1.1c.57-.23,1.19-.34,1.8-.24.15.03.31.08.45.15,1.43.17,2.83,1.16,3.57,2.32,2.09,3.28-2.13,8.31-4.93,7.08-.02,0-.03-.03-.04-.06h-.07c-.47-.11-.9-.33-1.28-.63-.21-.04-.41-.09-.61-.18-.43-.2-1.89-2.22-1.86-2.91l.4-.74c-.05-.1-.09-.2-.1-.3h0Z"/>
<circle cx="138.78" cy="47.52" r="10"/>
<circle class="cls-13" cx="251.08" cy="305.08" r="10"/>
<g>
<g>
<path class="cls-13" d="M137.4,243.49l-30.83-94.89,80.72-58.64c3.94-2.87,9.29-2.87,13.23,0l80.72,58.64-30.83,94.89c-1.51,4.64-5.83,7.78-10.7,7.78h-91.59c-4.88,0-9.2-3.14-10.7-7.78h-.02Z"/>
<g>
<path class="cls-13" d="M195.68,85.2c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-13" d="M282.89,148.58c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-13" d="M249.57,251.2c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-13" d="M141.64,251.24c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-13" d="M108.26,148.6c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
</g>
<circle class="cls-3" cx="193.91" cy="176.2" r="5"/>
</g>
<g class="cls-14">
<path class="cls-8" d="M137.11,243.18l-30.83-94.89,80.72-58.64c3.94-2.87,9.29-2.87,13.23,0l80.72,58.64-30.83,94.89c-1.51,4.64-5.83,7.78-10.7,7.78h-91.59c-4.88,0-9.2-3.14-10.7-7.78h-.02Z"/>
<path class="cls-8" d="M187.36,238.06l-49.79-35.92c-3.95-2.85-5.62-7.93-4.13-12.57l18.78-58.46c1.49-4.64,5.8-7.8,10.68-7.81l61.41-.2c4.87-.02,9.19,3.1,10.72,7.72l19.25,58.24c1.53,4.64-.1,9.73-4.04,12.61l-49.66,36.34c-3.93,2.88-9.28,2.9-13.23.04h.01Z"/>
<path class="cls-8" d="M212.51,216.61l-40.64-1.6c-4.89-.19-9.09-3.52-10.4-8.24l-10.86-39.18c-1.3-4.69.56-9.68,4.61-12.38l33.82-22.49c4.05-2.7,9.38-2.48,13.2.54l31.92,25.19c3.84,3.03,5.28,8.2,3.57,12.78l-14.24,38.07c-1.7,4.55-6.13,7.49-10.98,7.3h0Z"/>
<path class="cls-8" d="M219.56,194.36l-21.57,13.19c-4.16,2.54-9.48,2.12-13.19-1.05l-19.21-16.44c-3.7-3.17-4.95-8.36-3.08-12.87l9.7-23.35c1.87-4.5,6.43-7.29,11.29-6.9l25.21,2c4.86.39,8.92,3.86,10.05,8.6l5.88,24.59c1.13,4.74-.91,9.67-5.07,12.22h-.01Z"/>
<path class="cls-8" d="M214.74,176.18l-3.69,12.19c-1.41,4.67-5.67,7.89-10.54,7.99l-12.74.26c-4.87.1-9.26-2.95-10.86-7.56l-4.18-12.04c-1.6-4.61-.05-9.72,3.84-12.66l10.15-7.69c3.89-2.94,9.23-3.05,13.23-.27l10.45,7.28c4,2.79,5.75,7.83,4.34,12.5h0Z"/>
<path class="cls-8" d="M207.43,170.16l1.37,4.95c1.3,4.7-.58,9.7-4.64,12.39l-4.28,2.83c-4.07,2.69-9.41,2.45-13.22-.59l-4.02-3.2c-3.81-3.04-5.23-8.19-3.52-12.75l1.8-4.81c1.71-4.56,6.17-7.51,11.04-7.29l5.13.23c4.87.22,9.05,3.55,10.35,8.24h0Z"/>
<path class="cls-8" d="M202.16,169h0c3.28,3.61,3.87,8.92,1.46,13.15h0c-2.41,4.24-7.28,6.44-12.06,5.45h0c-4.77-.99-8.37-4.94-8.91-9.78h0c-.54-4.85,2.11-9.49,6.55-11.5h0c4.44-2.01,9.67-.93,12.96,2.68h0Z"/>
<path class="cls-7" d="M164.75,244.33l-44.12-61.11c-2.85-3.95-2.84-9.29.04-13.23l44.49-60.86c2.88-3.94,7.96-5.57,12.59-4.05l71.61,23.5c4.64,1.52,7.77,5.87,7.74,10.75l-.38,75.34c-.02,4.86-3.17,9.16-7.79,10.65l-71.6,23.13c-4.64,1.5-9.73-.16-12.58-4.12Z"/>
<path class="cls-7" d="M201.71,228.64l-47.54-17.41c-4.58-1.68-7.56-6.11-7.38-10.98l1.87-50.58c.18-4.87,3.48-9.07,8.17-10.41l48.71-13.86c4.68-1.33,9.68.49,12.41,4.53l28.33,41.95c2.74,4.05,2.54,9.41-.48,13.25l-31.37,39.91c-3.01,3.83-8.14,5.29-12.72,3.61h0Z"/>
<path class="cls-7" d="M217.58,205.34l-31.71,7.81c-4.76,1.17-9.74-.87-12.29-5.06l-17-27.81c-2.53-4.15-2.12-9.45,1.02-13.15l21.08-24.82c3.16-3.72,8.35-4.98,12.86-3.13l30.09,12.36c4.51,1.85,7.31,6.4,6.95,11.26l-2.46,32.46c-.37,4.85-3.81,8.91-8.53,10.08h-.01Z"/>
<path class="cls-7" d="M218.46,184.2l-11.67,14.58c-3.05,3.81-8.2,5.22-12.76,3.5l-17.47-6.59c-4.56-1.72-7.5-6.18-7.27-11.05l.87-18.65c.23-4.87,3.57-9.04,8.27-10.33l18.01-4.94c4.7-1.29,9.7.6,12.38,4.67l10.26,15.6c2.68,4.07,2.43,9.41-.62,13.22h0Z"/>
<path class="cls-7" d="M210.96,172.25v8.56c0,4.88-3.12,9.2-7.76,10.71l-8.13,2.65c-4.64,1.51-9.72-.13-12.59-4.08l-5.04-6.92c-2.87-3.94-2.88-9.28,0-13.23l5.02-6.93c2.86-3.95,7.94-5.6,12.58-4.1l8.14,2.63c4.64,1.5,7.78,5.82,7.79,10.69v.02Z"/>
<path class="cls-7" d="M204.48,169.3l1.13,2.01c2.39,4.25,1.78,9.56-1.53,13.14l-1.56,1.7c-3.3,3.59-8.54,4.64-12.97,2.61l-2.1-.96c-4.43-2.03-7.05-6.69-6.49-11.53l.27-2.29c.56-4.84,4.18-8.77,8.96-9.73l2.27-.46c4.78-.96,9.64,1.26,12.03,5.51h-.01Z"/>
<g>
<path class="cls-4" d="M195.39,84.89c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-4" d="M282.6,148.26c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-4" d="M249.28,250.89c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-4" d="M141.35,250.93c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
<path class="cls-4" d="M107.97,148.29c0,.94-.76,1.7-1.7,1.7s-1.7-.76-1.7-1.7.76-1.7,1.7-1.7,1.7.76,1.7,1.7Z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 805 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 KiB

View File

@ -0,0 +1,193 @@
<svg viewBox="0 0 575 250" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="sG3" x1="0" y1="0" x2=".5" y2="1">
<stop offset="0%" stop-color="#E6ECF2"/>
<stop offset="50%" stop-color="#BCC8D4"/>
<stop offset="100%" stop-color="#8090A4"/>
</linearGradient>
<linearGradient id="aG3" x1=".3" y1="0" x2=".8" y2="1">
<stop offset="0%" stop-color="#B87000"/>
<stop offset="50%" stop-color="#E0B040"/>
<stop offset="100%" stop-color="#F8D860"/>
</linearGradient>
<linearGradient id="aN3" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#F5C44A"/>
<stop offset="100%" stop-color="#DC9018"/>
</linearGradient>
<linearGradient id="sN3" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#A8B4C8"/>
<stop offset="100%" stop-color="#5A6A7C"/>
</linearGradient>
<clipPath id="sC3">
<path d="M300,75 C405,195 195,345 300,465 L162,408 L105,270 L162,132 Z"/>
</clipPath>
<clipPath id="aC3">
<path d="M300,75 C405,195 195,345 300,465 L438,408 L495,270 L438,132 Z"/>
</clipPath>
<filter id="nGl3" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="4" result="b"/>
<feFlood flood-color="#E8A838" flood-opacity=".1"/>
<feComposite in2="b" operator="in" result="g"/>
<feComposite in="SourceGraphic" in2="g" operator="over"/>
</filter>
<filter id="nSh3"><feDropShadow dx="0" dy="2" stdDeviation="3.5" flood-color="#DC9018" flood-opacity=".28"/></filter>
<filter id="sGl3"><feGaussianBlur stdDeviation="1.5" result="b"/><feComposite in="SourceGraphic" in2="b" operator="over"/></filter>
<filter id="cGl3"><feGaussianBlur stdDeviation="3" result="b"/><feComposite in="SourceGraphic" in2="b" operator="over"/></filter>
</defs>
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@500;600&amp;display=swap');
@keyframes bp3{0%,100%{opacity:calc(var(--o,.82) - .20)}50%{opacity:var(--o,.82)}}
@keyframes nf3{0%,100%{transform:translate(0,0)}50%{transform:translate(0,-2px)}}
@keyframes np3{0%,100%{r:var(--r,17)}50%{r:calc(var(--r,17) + 1.8)}}
@keyframes de3{0%{stroke-dashoffset:210}100%{stroke-dashoffset:0}}
@keyframes eb3{0%,100%{opacity:.48}50%{opacity:.88}}
@keyframes ap3{0%,100%{opacity:.42}50%{opacity:.92}}
@keyframes seedAmber{0%,100%{opacity:.72;r:16}50%{opacity:.88;r:18}}
@keyframes seedSilver{0%,100%{opacity:.32}50%{opacity:.52}}
@keyframes fl3{0%,100%{transform:translateY(0)}50%{transform:translateY(-3px)}}
@keyframes fi3{0%{opacity:0;transform:translateY(10px)}100%{opacity:1;transform:translateY(0)}}
@keyframes cB3{0%,100%{stroke-opacity:.1}50%{stroke-opacity:.2}}
@keyframes rP3{0%,100%{stroke-opacity:.04}50%{stroke-opacity:.068}}
@keyframes tP{0%,100%{opacity:calc(var(--to,.08)*5)}50%{opacity:calc(var(--to,.08)*5 + .25)}}
@keyframes cD{0%,100%{r:2.5;opacity:.60}50%{r:3.5;opacity:.80}}
.bp3{animation:bp3 4.5s ease-in-out infinite}
.nf3{animation:nf3 5s ease-in-out infinite}
.np3{animation:np3 3.5s ease-in-out infinite}
.de3{stroke-dasharray:210;animation:de3 1.8s ease-out forwards,eb3 4s ease-in-out 2s infinite}
.ap3{animation:ap3 3s ease-in-out infinite}
.sAm{animation:seedAmber 4s ease-in-out infinite}
.sSi{animation:seedSilver 4s ease-in-out infinite}
.fl3{animation:fl3 7s ease-in-out infinite}
.fi3{animation:fi3 1s ease-out .5s both}
.cB3{animation:cB3 5s ease-in-out infinite}
.rP3{animation:rP3 7s ease-in-out infinite}
.tP{animation:tP 5.5s ease-in-out infinite}
.cD{animation:cD 4s ease-in-out infinite}
</style>
<!-- ═══ IMAGE GROUP ═══ -->
<g id="image-group" transform="translate(-30, -22) scale(0.50)">
<!-- ═══ PAKUA FRAME ═══ -->
<path d="M300,52 L454,117 L518,270 L454,423 L300,488 L146,423 L82,270 L146,117 Z"
fill="none" stroke="#E8A838" stroke-width="1.5" stroke-opacity=".35"/>
<path d="M300,62 L447,124 L508,270 L447,416 L300,478 L153,416 L92,270 L153,124 Z"
fill="none" stroke="#C0CCD8" stroke-width="1.2" stroke-opacity=".30"/>
<!-- Trigram marks -->
<g class="tP" style="--to:.1">
<line x1="278" y1="50" x2="322" y2="50" stroke="#C0CCD8" stroke-width="2.2" stroke-linecap="round"/>
<line x1="281" y1="57" x2="319" y2="57" stroke="#C0CCD8" stroke-width="1.8" stroke-linecap="round"/>
<line x1="284" y1="63" x2="316" y2="63" stroke="#C0CCD8" stroke-width="1.5" stroke-linecap="round"/>
</g>
<g class="tP" style="--to:.08;animation-delay:2.75s">
<line x1="280" y1="488" x2="296" y2="488" stroke="#E8A838" stroke-width="2.2" stroke-linecap="round"/>
<line x1="304" y1="488" x2="320" y2="488" stroke="#E8A838" stroke-width="2.2" stroke-linecap="round"/>
<line x1="282" y1="494" x2="295" y2="494" stroke="#E8A838" stroke-width="1.8" stroke-linecap="round"/>
<line x1="305" y1="494" x2="318" y2="494" stroke="#E8A838" stroke-width="1.8" stroke-linecap="round"/>
</g>
<g class="tP" style="--to:.07;animation-delay:.7s">
<line x1="518" y1="252" x2="518" y2="266" stroke="#E8A838" stroke-width="2" stroke-linecap="round"/>
<line x1="518" y1="274" x2="518" y2="278" stroke="#E8A838" stroke-width="1.5" stroke-linecap="round"/>
<line x1="518" y1="286" x2="518" y2="300" stroke="#E8A838" stroke-width="2" stroke-linecap="round"/>
</g>
<g class="tP" style="--to:.07;animation-delay:2s">
<line x1="82" y1="252" x2="82" y2="260" stroke="#C0CCD8" stroke-width="2" stroke-linecap="round"/>
<line x1="82" y1="268" x2="82" y2="282" stroke="#C0CCD8" stroke-width="2" stroke-linecap="round"/>
<line x1="82" y1="290" x2="82" y2="298" stroke="#C0CCD8" stroke-width="2" stroke-linecap="round"/>
</g>
<g class="tP" style="--to:.06;animation-delay:1.1s">
<line x1="462" y1="110" x2="472" y2="118" stroke="#E8A838" stroke-width="2" stroke-linecap="round"/>
<line x1="458" y1="103" x2="468" y2="111" stroke="#E8A838" stroke-width="1.6" stroke-linecap="round"/>
</g>
<g class="tP" style="--to:.06;animation-delay:3.8s">
<line x1="128" y1="118" x2="138" y2="110" stroke="#C0CCD8" stroke-width="2" stroke-linecap="round"/>
<line x1="132" y1="111" x2="142" y2="103" stroke="#C0CCD8" stroke-width="1.6" stroke-linecap="round"/>
</g>
<g class="tP" style="--to:.06;animation-delay:4.4s">
<line x1="462" y1="430" x2="472" y2="422" stroke="#E8A838" stroke-width="2" stroke-linecap="round"/>
<line x1="458" y1="437" x2="468" y2="429" stroke="#E8A838" stroke-width="1.6" stroke-linecap="round"/>
</g>
<g class="tP" style="--to:.06;animation-delay:5s">
<line x1="128" y1="422" x2="138" y2="430" stroke="#C0CCD8" stroke-width="2" stroke-linecap="round"/>
<line x1="132" y1="429" x2="142" y2="437" stroke="#C0CCD8" stroke-width="1.6" stroke-linecap="round"/>
</g>
<!-- Corner dots -->
<circle class="cD" cx="300" cy="52" r="2.5" fill="#C0CCD8" style="animation-delay:0s"/>
<circle class="cD" cx="454" cy="117" r="2.5" fill="#E8A838" style="animation-delay:.55s"/>
<circle class="cD" cx="518" cy="270" r="2.5" fill="#E8A838" style="animation-delay:1.1s"/>
<circle class="cD" cx="454" cy="423" r="2.5" fill="#E8A838" style="animation-delay:1.65s"/>
<circle class="cD" cx="300" cy="488" r="2.5" fill="#E8A838" style="animation-delay:2.2s"/>
<circle class="cD" cx="146" cy="423" r="2.5" fill="#C0CCD8" style="animation-delay:2.75s"/>
<circle class="cD" cx="82" cy="270" r="2.5" fill="#C0CCD8" style="animation-delay:3.3s"/>
<circle class="cD" cx="146" cy="117" r="2.5" fill="#C0CCD8" style="animation-delay:3.85s"/>
<!-- MAIN SYMBOL -->
<g class="fl3">
<path d="M300,75 L438,132 L495,270 L438,408 L300,465 L162,408 L105,270 L162,132 Z"
fill="none" stroke="rgba(255,255,255,.055)" stroke-width="1.5"/>
<!-- SILVER REGION -->
<g clip-path="url(#sC3)">
<path d="M300,75 L162,132 L105,270 L162,408 L300,465 C195,345 405,195 300,75Z" fill="url(#sG3)" opacity=".93"/>
<rect class="bp3" x="165" y="160" width="52" height="40" rx="4" fill="url(#aG3)" stroke="rgba(255,255,255,.25)" stroke-width="1" opacity=".88" style="--o:.88;animation-delay:0.9s"/>
<rect class="bp3" x="235" y="223" width="48" height="38" rx="4" fill="url(#aG3)" stroke="rgba(255,255,255,.25)" stroke-width="1" opacity=".82" style="--o:.82;animation-delay:0.7s"/>
<rect class="bp3" x="163" y="220" width="55" height="46" rx="4" fill="url(#aG3)" stroke="rgba(255,255,255,.25)" stroke-width="1" opacity=".74" style="--o:.74;animation-delay:0.5s"/>
<rect class="bp3" x="168" y="345" width="25" height="18" rx="4" fill="url(#aG3)" stroke="rgba(255,255,255,.25)" stroke-width="1" opacity=".70" style="--o:.70;animation-delay:0.15s"/>
<rect class="bp3" x="220" y="290" width="35" height="38" rx="4" fill="url(#aG3)" stroke="rgba(255,255,255,.25)" stroke-width="1" opacity=".74" style="--o:.74;animation-delay:0.3s"/>
<rect class="bp3" x="160" y="285" width="40" height="32" rx="4" fill="url(#aG3)" stroke="rgba(255,255,255,.25)" stroke-width="1" opacity=".64" style="--o:.64;animation-delay:0s"/>
<rect class="bp3" x="220" y="350" width="28" height="24" rx="4" fill="url(#aG3)" stroke="rgba(255,255,255,.25)" stroke-width="1" opacity=".64" style="--o:.64;animation-delay:.8s"/>
<circle class="sAm" cx="265" cy="170" r="16" fill="url(#aN3)" opacity=".72" stroke="rgba(255,255,255,.3)" stroke-width="1.5"/>
</g>
<!-- AMBER REGION -->
<g clip-path="url(#aC3)">
<path d="M300,75 L438,132 L495,270 L438,408 L300,465 C195,345 405,195 300,75Z" fill="url(#aG3)" opacity=".88"/>
<g filter="url(#nGl3)">
<line class="de3" x1="386" y1="162" x2="355" y2="214" stroke="#8090A4" stroke-width="4.5" stroke-linecap="round" opacity=".45" style="animation-delay:.15s"/>
<line class="de3" x1="405" y1="162" x2="440" y2="214" stroke="#8090A4" stroke-width="4.5" stroke-linecap="round" opacity=".45" style="animation-delay:.35s"/>
<line class="de3" x1="364" y1="247" x2="398" y2="294" stroke="#8090A4" stroke-width="4" stroke-linecap="round" opacity=".4" style="animation-delay:.6s"/>
<line class="de3" x1="431" y1="247" x2="398" y2="294" stroke="#8090A4" stroke-width="4" stroke-linecap="round" opacity=".4" style="animation-delay:.8s"/>
<line class="de3" x1="394" y1="331" x2="385" y2="368" stroke="#8090A4" stroke-width="3.5" stroke-linecap="round" opacity=".35" style="animation-delay:1.05s"/>
<polygon class="ap3" points="349,210 357,222 363,212" fill="#8090A4" opacity=".45" style="animation-delay:.15s"/>
<polygon class="ap3" points="434,210 442,222 448,210" fill="#8090A4" opacity=".45" style="animation-delay:.35s"/>
<polygon class="ap3" points="392,290 400,302 406,290" fill="#8090A4" opacity=".4" style="animation-delay:.6s"/>
<polygon class="ap3" points="379,365 387,377 393,365" fill="#8090A4" opacity=".35" style="animation-delay:1.05s"/>
</g>
<g class="nf3" style="animation-delay:0s">
<circle class="np3" cx="395" cy="148" r="17" fill="url(#sN3)" stroke="rgba(255,255,255,.3)" stroke-width="1.5" opacity=".75" style="--r:17"/>
</g>
<g class="nf3" style="animation-delay:.3s">
<circle class="np3" cx="355" cy="236" r="14" fill="url(#sN3)" stroke="rgba(255,255,255,.3)" stroke-width="1.5" opacity=".7" style="--r:14"/>
</g>
<g class="nf3" style="animation-delay:.5s">
<circle class="np3" cx="440" cy="236" r="14" fill="url(#sN3)" stroke="rgba(255,255,255,.3)" stroke-width="1.5" opacity=".7" style="--r:14"/>
</g>
<g class="nf3" style="animation-delay:.8s">
<circle class="np3" cx="398" cy="316" r="15" fill="url(#sN3)" stroke="rgba(255,255,255,.3)" stroke-width="1.5" opacity=".72" style="--r:15"/>
</g>
<g class="nf3" style="animation-delay:1.1s">
<circle class="np3" cx="385" cy="390" r="12" fill="url(#sN3)" stroke="rgba(255,255,255,.3)" stroke-width="1.5" opacity=".65" style="--r:12"/>
</g>
<rect class="sAm" x="320" y="330" width="26" height="26" rx="3" fill="#5A6A7C" opacity=".85" stroke="rgba(255,255,255,.35)" stroke-width="1.5"/>
</g>
<!-- S-CURVE -->
<path class="cB3" d="M300,75 C405,195 195,345 300,465"
fill="none" stroke="rgba(255,255,255,.1)" stroke-width="1.8" filter="url(#cGl3)"/>
</g>
</g>
<!-- ═══ TEXT GROUP ═══ -->
<g id="text-group" class="fi3">
<text x="400" y="131" text-anchor="middle" font-family="'IBM Plex Mono',monospace" font-size="72" font-weight="600" letter-spacing=".03em">
<tspan fill="#C0CCD8">Onto</tspan><tspan fill="#E8A838">ref</tspan>
</text>
<text x="398" y="156" text-anchor="middle" font-family="'IBM Plex Sans',sans-serif" font-size="16" font-weight="500" fill="#A8B4C8" letter-spacing=".03em" opacity=".9">
Structure that remembers why
</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,19 @@
<svg viewBox="0 0 400 180" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@500;600&amp;display=swap');
@keyframes fi3{0%{opacity:0;transform:translateY(10px)}100%{opacity:1;transform:translateY(0)}}
.fi3{animation:fi3 1s ease-out .5s both}
</style>
</defs>
<!-- Wordmark -->
<g class="fi3">
<text x="200" y="85" text-anchor="middle" font-family="'IBM Plex Mono',monospace" font-size="92" font-weight="600" letter-spacing=".03em">
<tspan fill="#C0CCD8">Onto</tspan><tspan fill="#E8A838">ref</tspan>
</text>
<text x="200" y="145" text-anchor="middle" font-family="'IBM Plex Sans',sans-serif" font-size="22" font-weight="500" fill="#A8B4C8" letter-spacing=".03em" opacity=".9">
Structure that remembers why.
</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 878 B

View File

@ -0,0 +1,197 @@
<svg viewBox="0 0 600 700" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="sG3" x1="0" y1="0" x2=".5" y2="1">
<stop offset="0%" stop-color="#E6ECF2"/>
<stop offset="50%" stop-color="#BCC8D4"/>
<stop offset="100%" stop-color="#8090A4"/>
</linearGradient>
<linearGradient id="aG3" x1=".3" y1="0" x2=".8" y2="1">
<stop offset="0%" stop-color="#B87000"/>
<stop offset="50%" stop-color="#E0B040"/>
<stop offset="100%" stop-color="#F8D860"/>
</linearGradient>
<linearGradient id="aN3" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#F5C44A"/>
<stop offset="100%" stop-color="#DC9018"/>
</linearGradient>
<linearGradient id="sN3" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#A8B4C8"/>
<stop offset="100%" stop-color="#5A6A7C"/>
</linearGradient>
<clipPath id="sC3">
<path d="M300,75 C405,195 195,345 300,465 L162,408 L105,270 L162,132 Z"/>
</clipPath>
<clipPath id="aC3">
<path d="M300,75 C405,195 195,345 300,465 L438,408 L495,270 L438,132 Z"/>
</clipPath>
<filter id="nGl3" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="4" result="b"/>
<feFlood flood-color="#E8A838" flood-opacity=".1"/>
<feComposite in2="b" operator="in" result="g"/>
<feComposite in="SourceGraphic" in2="g" operator="over"/>
</filter>
<filter id="nSh3"><feDropShadow dx="0" dy="2" stdDeviation="3.5" flood-color="#DC9018" flood-opacity=".28"/></filter>
<filter id="sGl3"><feGaussianBlur stdDeviation="1.5" result="b"/><feComposite in="SourceGraphic" in2="b" operator="over"/></filter>
<filter id="cGl3"><feGaussianBlur stdDeviation="3" result="b"/><feComposite in="SourceGraphic" in2="b" operator="over"/></filter>
</defs>
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@500;600&amp;display=swap');
@keyframes bp3{0%,100%{opacity:calc(var(--o,.82) - .20)}50%{opacity:var(--o,.82)}}
@keyframes nf3{0%,100%{transform:translate(0,0)}50%{transform:translate(0,-2px)}}
@keyframes np3{0%,100%{r:var(--r,17)}50%{r:calc(var(--r,17) + 1.8)}}
@keyframes de3{0%{stroke-dashoffset:210}100%{stroke-dashoffset:0}}
@keyframes eb3{0%,100%{opacity:.48}50%{opacity:.88}}
@keyframes ap3{0%,100%{opacity:.42}50%{opacity:.92}}
@keyframes seedAmber{0%,100%{opacity:.72;r:16}50%{opacity:.88;r:18}}
@keyframes seedSilver{0%,100%{opacity:.32}50%{opacity:.52}}
@keyframes fl3{0%,100%{transform:translateY(0)}50%{transform:translateY(-3px)}}
@keyframes cB3{0%,100%{stroke-opacity:.1}50%{stroke-opacity:.2}}
@keyframes rP3{0%,100%{stroke-opacity:.04}50%{stroke-opacity:.068}}
@keyframes tP{0%,100%{opacity:calc(var(--to,.08)*5)}50%{opacity:calc(var(--to,.08)*5 + .25)}}
@keyframes cD{0%,100%{r:2.5;opacity:.60}50%{r:3.5;opacity:.80}}
.bp3{animation:bp3 4.5s ease-in-out infinite}
.nf3{animation:nf3 5s ease-in-out infinite}
.np3{animation:np3 3.5s ease-in-out infinite}
.de3{stroke-dasharray:210;animation:de3 1.8s ease-out forwards,eb3 4s ease-in-out 2s infinite}
.ap3{animation:ap3 3s ease-in-out infinite}
.sAm{animation:seedAmber 4s ease-in-out infinite}
.sSi{animation:seedSilver 4s ease-in-out infinite}
.fl3{animation:fl3 7s ease-in-out infinite}
.cB3{animation:cB3 5s ease-in-out infinite}
.rP3{animation:rP3 7s ease-in-out infinite}
.tP{animation:tP 5.5s ease-in-out infinite}
.cD{animation:cD 4s ease-in-out infinite}
</style>
<!-- ═══ PAKUA FRAME ═══ -->
<!-- Outer octagon R=218 -->
<path d="M300,52 L454,117 L518,270 L454,423 L300,488 L146,423 L82,270 L146,117 Z"
fill="none" stroke="#E8A838" stroke-width="1.5" stroke-opacity=".35"/>
<!-- Inner decorative ring R=208 -->
<path d="M300,62 L447,124 L508,270 L447,416 L300,478 L153,416 L92,270 L153,124 Z"
fill="none" stroke="#C0CCD8" stroke-width="1.2" stroke-opacity=".30"/>
<!-- Trigram marks at 8 positions -->
<!-- Top (☰ heaven: 3 solid) — silver -->
<g class="tP" style="--to:.1">
<line x1="278" y1="50" x2="322" y2="50" stroke="#C0CCD8" stroke-width="2.2" stroke-linecap="round"/>
<line x1="281" y1="57" x2="319" y2="57" stroke="#C0CCD8" stroke-width="1.8" stroke-linecap="round"/>
<line x1="284" y1="63" x2="316" y2="63" stroke="#C0CCD8" stroke-width="1.5" stroke-linecap="round"/>
</g>
<!-- Bottom (☷ earth: 3 broken) — amber -->
<g class="tP" style="--to:.08;animation-delay:2.75s">
<line x1="280" y1="488" x2="296" y2="488" stroke="#E8A838" stroke-width="2.2" stroke-linecap="round"/>
<line x1="304" y1="488" x2="320" y2="488" stroke="#E8A838" stroke-width="2.2" stroke-linecap="round"/>
<line x1="282" y1="494" x2="295" y2="494" stroke="#E8A838" stroke-width="1.8" stroke-linecap="round"/>
<line x1="305" y1="494" x2="318" y2="494" stroke="#E8A838" stroke-width="1.8" stroke-linecap="round"/>
</g>
<!-- Right (☲ fire) — amber -->
<g class="tP" style="--to:.07;animation-delay:.7s">
<line x1="518" y1="252" x2="518" y2="266" stroke="#E8A838" stroke-width="2" stroke-linecap="round"/>
<line x1="518" y1="274" x2="518" y2="278" stroke="#E8A838" stroke-width="1.5" stroke-linecap="round"/>
<line x1="518" y1="286" x2="518" y2="300" stroke="#E8A838" stroke-width="2" stroke-linecap="round"/>
</g>
<!-- Left (☵ water) — silver -->
<g class="tP" style="--to:.07;animation-delay:2s">
<line x1="82" y1="252" x2="82" y2="260" stroke="#C0CCD8" stroke-width="2" stroke-linecap="round"/>
<line x1="82" y1="268" x2="82" y2="282" stroke="#C0CCD8" stroke-width="2" stroke-linecap="round"/>
<line x1="82" y1="290" x2="82" y2="298" stroke="#C0CCD8" stroke-width="2" stroke-linecap="round"/>
</g>
<!-- TR, TL, BR, BL — alternating -->
<g class="tP" style="--to:.06;animation-delay:1.1s">
<line x1="462" y1="110" x2="472" y2="118" stroke="#E8A838" stroke-width="2" stroke-linecap="round"/>
<line x1="458" y1="103" x2="468" y2="111" stroke="#E8A838" stroke-width="1.6" stroke-linecap="round"/>
</g>
<g class="tP" style="--to:.06;animation-delay:3.8s">
<line x1="128" y1="118" x2="138" y2="110" stroke="#C0CCD8" stroke-width="2" stroke-linecap="round"/>
<line x1="132" y1="111" x2="142" y2="103" stroke="#C0CCD8" stroke-width="1.6" stroke-linecap="round"/>
</g>
<g class="tP" style="--to:.06;animation-delay:4.4s">
<line x1="462" y1="430" x2="472" y2="422" stroke="#E8A838" stroke-width="2" stroke-linecap="round"/>
<line x1="458" y1="437" x2="468" y2="429" stroke="#E8A838" stroke-width="1.6" stroke-linecap="round"/>
</g>
<g class="tP" style="--to:.06;animation-delay:5s">
<line x1="128" y1="422" x2="138" y2="430" stroke="#C0CCD8" stroke-width="2" stroke-linecap="round"/>
<line x1="132" y1="429" x2="142" y2="437" stroke="#C0CCD8" stroke-width="1.6" stroke-linecap="round"/>
</g>
<!-- Corner dots -->
<circle class="cD" cx="300" cy="52" r="2.5" fill="#C0CCD8" style="animation-delay:0s"/>
<circle class="cD" cx="454" cy="117" r="2.5" fill="#E8A838" style="animation-delay:.55s"/>
<circle class="cD" cx="518" cy="270" r="2.5" fill="#E8A838" style="animation-delay:1.1s"/>
<circle class="cD" cx="454" cy="423" r="2.5" fill="#E8A838" style="animation-delay:1.65s"/>
<circle class="cD" cx="300" cy="488" r="2.5" fill="#E8A838" style="animation-delay:2.2s"/>
<circle class="cD" cx="146" cy="423" r="2.5" fill="#C0CCD8" style="animation-delay:2.75s"/>
<circle class="cD" cx="82" cy="270" r="2.5" fill="#C0CCD8" style="animation-delay:3.3s"/>
<circle class="cD" cx="146" cy="117" r="2.5" fill="#C0CCD8" style="animation-delay:3.85s"/>
<!-- ═══ MAIN SYMBOL ═══ -->
<g class="fl3" transform="scale(0.65) translate(64, 0)">
<!-- Octagon outline R=195 -->
<path d="M300,75 L438,132 L495,270 L438,408 L300,465 L162,408 L105,270 L162,132 Z"
fill="none" stroke="rgba(255,255,255,.055)" stroke-width="1.5"/>
<!-- SILVER REGION (Ref / Structure) -->
<g clip-path="url(#sC3)">
<path d="M300,75 L162,132 L105,270 L162,408 L300,465 C195,345 405,195 300,75Z" fill="url(#sG3)" opacity=".93"/>
<!-- Grid blocks (two columns) -->
<rect class="bp3" x="165" y="160" width="52" height="40" rx="4" fill="url(#aG3)" stroke="rgba(255,255,255,.25)" stroke-width="1" opacity=".88" style="--o:.88;animation-delay:0.9s"/>
<rect class="bp3" x="235" y="223" width="48" height="38" rx="4" fill="url(#aG3)" stroke="rgba(255,255,255,.25)" stroke-width="1" opacity=".82" style="--o:.82;animation-delay:0.7s"/>
<rect class="bp3" x="163" y="220" width="55" height="46" rx="4" fill="url(#aG3)" stroke="rgba(255,255,255,.25)" stroke-width="1" opacity=".74" style="--o:.74;animation-delay:0.5s"/>
<rect class="bp3" x="168" y="345" width="25" height="18" rx="4" fill="url(#aG3)" stroke="rgba(255,255,255,.25)" stroke-width="1" opacity=".70" style="--o:.70;animation-delay:0.15s"/>
<rect class="bp3" x="220" y="290" width="35" height="38" rx="4" fill="url(#aG3)" stroke="rgba(255,255,255,.25)" stroke-width="1" opacity=".74" style="--o:.74;animation-delay:0.3s"/>
<rect class="bp3" x="160" y="285" width="40" height="32" rx="4" fill="url(#aG3)" stroke="rgba(255,255,255,.25)" stroke-width="1" opacity=".64" style="--o:.64;animation-delay:0s"/>
<rect class="bp3" x="220" y="350" width="28" height="24" rx="4" fill="url(#aG3)" stroke="rgba(255,255,255,.25)" stroke-width="1" opacity=".64" style="--o:.64;animation-delay:.8s"/>
<!-- ★ SEED: amber circle in silver -->
<circle class="sAm" cx="265" cy="170" r="16" fill="url(#aN3)" opacity=".72" stroke="rgba(255,255,255,.3)" stroke-width="1.5"/>
</g>
<!-- AMBER REGION (Onto / Graph) -->
<g clip-path="url(#aC3)">
<path d="M300,75 L438,132 L495,270 L438,408 L300,465 C195,345 405,195 300,75Z" fill="url(#aG3)" opacity=".88"/>
<!-- DAG -->
<g filter="url(#nGl3)">
<!-- Line 1: 395,155 → 355,236 (nodo 2, r=14) termina antes del nodo -->
<line class="de3" x1="386" y1="162" x2="355" y2="214" stroke="#8090A4" stroke-width="4.5" stroke-linecap="round" opacity=".45" style="animation-delay:.15s"/>
<!-- Line 2: 395,155 → 440,236 (nodo 3, r=14) termina antes del nodo -->
<line class="de3" x1="405" y1="162" x2="440" y2="214" stroke="#8090A4" stroke-width="4.5" stroke-linecap="round" opacity=".45" style="animation-delay:.35s"/>
<!-- Line 3: 355,244 → 398,316 (nodo 4, r=15) termina antes del nodo -->
<line class="de3" x1="364" y1="247" x2="398" y2="294" stroke="#8090A4" stroke-width="4" stroke-linecap="round" opacity=".4" style="animation-delay:.6s"/>
<!-- Line 4: 440,244 → 398,316 (nodo 4, r=15) termina antes del nodo -->
<line class="de3" x1="431" y1="247" x2="398" y2="294" stroke="#8090A4" stroke-width="4" stroke-linecap="round" opacity=".4" style="animation-delay:.8s"/>
<!-- Line 5: 398,324 → 385,390 (nodo 5, r=12) termina antes del nodo -->
<line class="de3" x1="394" y1="331" x2="385" y2="368" stroke="#8090A4" stroke-width="3.5" stroke-linecap="round" opacity=".35" style="animation-delay:1.05s"/>
<!-- Arrowheads -->
<polygon class="ap3" points="349,210 357,222 363,212" fill="#8090A4" opacity=".45" style="animation-delay:.15s"/>
<polygon class="ap3" points="434,210 442,222 448,210" fill="#8090A4" opacity=".45" style="animation-delay:.35s"/>
<polygon class="ap3" points="392,290 400,302 406,290" fill="#8090A4" opacity=".4" style="animation-delay:.6s"/>
<polygon class="ap3" points="379,365 387,377 393,365" fill="#8090A4" opacity=".35" style="animation-delay:1.05s"/>
</g>
<!-- Nodes (silver gradient) -->
<g class="nf3" style="animation-delay:0s">
<circle class="np3" cx="395" cy="148" r="17" fill="url(#sN3)" stroke="rgba(255,255,255,.3)" stroke-width="1.5" opacity=".75" style="--r:17"/>
</g>
<g class="nf3" style="animation-delay:.3s">
<circle class="np3" cx="355" cy="236" r="14" fill="url(#sN3)" stroke="rgba(255,255,255,.3)" stroke-width="1.5" opacity=".7" style="--r:14"/>
</g>
<g class="nf3" style="animation-delay:.5s">
<circle class="np3" cx="440" cy="236" r="14" fill="url(#sN3)" stroke="rgba(255,255,255,.3)" stroke-width="1.5" opacity=".7" style="--r:14"/>
</g>
<g class="nf3" style="animation-delay:.8s">
<circle class="np3" cx="398" cy="316" r="15" fill="url(#sN3)" stroke="rgba(255,255,255,.3)" stroke-width="1.5" opacity=".72" style="--r:15"/>
</g>
<g class="nf3" style="animation-delay:1.1s">
<circle class="np3" cx="385" cy="390" r="12" fill="url(#sN3)" stroke="rgba(255,255,255,.3)" stroke-width="1.5" opacity=".65" style="--r:12"/>
</g>
<!-- ★ SEED: silver square in amber -->
<rect class="sAm" x="320" y="330" width="26" height="26" rx="3" fill="#5A6A7C" opacity=".85" stroke="rgba(255,255,255,.35)" stroke-width="1.5"/>
</g>
<!-- S-CURVE -->
<path class="cB3" d="M300,75 C405,195 195,345 300,465"
fill="none" stroke="rgba(255,255,255,.1)" stroke-width="1.8" filter="url(#cGl3)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,19 @@
<svg viewBox="0 0 400 150" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@500;600&amp;display=swap');
@keyframes fi3{0%{opacity:0;transform:translateY(0px)}100%{opacity:1;transform:translateY(0)}}
.fi3{animation:fi3 1s ease-out .5s both}
</style>
</defs>
<!-- Wordmark -->
<g class="fi3">
<text x="200" y="55" text-anchor="middle" font-family="'IBM Plex Mono',monospace" font-size="92" font-weight="600" letter-spacing=".03em">
<tspan fill="#C0CCD8">Onto</tspan><tspan fill="#E8A838">ref</tspan>
</text>
<text x="200" y="105" text-anchor="middle" font-family="'IBM Plex Sans',sans-serif" font-size="22" font-weight="500" fill="#A8B4C8" letter-spacing=".03em" opacity=".9">
Structure that remembers why.
</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 877 B

View File

@ -0,0 +1,161 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="b" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 359.49 230.13">
<defs>
<clipPath id="d">
<path d="M226.79,113.62c0,19.36,15.85,35.06,35.4,35.06,19.55,0,35.4-15.7,35.4-35.06,0-19.36-15.85-35.06-35.4-35.06h0c-42.68,0-35.4,15.7-35.4,35.06" style="fill:none;"/>
</clipPath>
<!-- Animated gradient for connecting elements -->
<linearGradient id="infinityGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#00D4FF;stop-opacity:1">
<animate attributeName="stop-color" values="#00D4FF;#7B2BFF;#00FFB3;#00D4FF" dur="4s" repeatCount="indefinite"/>
</stop>
<stop offset="50%" style="stop-color:#7B2BFF;stop-opacity:1">
<animate attributeName="stop-color" values="#7B2BFF;#00FFB3;#00D4FF;#7B2BFF" dur="4s" repeatCount="indefinite"/>
</stop>
<stop offset="100%" style="stop-color:#00FFB3;stop-opacity:1">
<animate attributeName="stop-color" values="#00FFB3;#00D4FF;#7B2BFF;#00FFB3" dur="4s" repeatCount="indefinite"/>
</stop>
</linearGradient>
</defs>
<g id="c">
<g>
<!-- Left wheel - only outer shape rotates -->
<g>
<path d="M3.04,87.53c-3.66,16.4-3.36,33.43,.85,49.7l18.49-.31c2.12,7.08,5.09,13.89,8.85,20.26l-12.81,13.12c9.22,14.08,21.56,25.9,36.09,34.55l12.81-13.13c6.56,3.51,13.52,6.22,20.74,8.08l.31,18.3c16.56,3.62,33.75,3.33,50.18-.85l-.31-18.3c7.15-2.1,14.02-5.04,20.46-8.77l13.25,12.69c14.22-9.14,26.14-21.36,34.87-35.75l-13.25-12.68c3.54-6.5,6.28-13.4,8.15-20.55l18.49-.31c3.65-16.4,3.36-33.43-.85-49.7l-18.49,.31c-2.12-7.08-5.09-13.88-8.85-20.26l12.8-13.12c-9.22-14.08-21.56-25.9-36.08-34.55l-12.81,13.13c-6.56-3.51-13.53-6.23-20.75-8.08l-.31-18.3c-16.56-3.62-33.75-3.33-50.18,.85l.31,18.3c-7.15,2.1-14.01,5.04-20.45,8.77l-13.25-12.69c-14.22,9.14-26.15,21.36-34.88,35.75l13.25,12.68c-3.54,6.5-6.28,13.4-8.15,20.55l-18.49,.31Z" style="fill:#4cc2f1; fill-rule:evenodd; stroke:#fff; stroke-miterlimit:10;">
<animateTransform
attributeName="transform"
type="rotate"
values="0 115.86 113.25;360 115.86 113.25"
dur="8s"
repeatCount="indefinite"/>
</path>
</g>
<!-- Right wheel - only outer shape rotates -->
<g>
<path d="M167.28,92.87c-3.19,14.31-2.94,29.17,.74,43.37l16.13-.27c1.85,6.18,4.44,12.12,7.72,17.68l-11.18,11.45c8.05,12.29,18.82,22.6,31.49,30.15l11.18-11.46c5.72,3.06,11.8,5.43,18.1,7.05l.27,15.97c14.45,3.16,29.45,2.91,43.78-.74l-.27-15.97c6.24-1.83,12.23-4.4,17.85-7.66l11.56,11.08c12.41-7.97,22.81-18.64,30.43-31.19l-11.56-11.06c3.09-5.67,5.48-11.69,7.11-17.93l16.13-.27c3.18-14.31,2.93-29.17-.74-43.37l-16.13,.27c-1.85-6.18-4.44-12.11-7.72-17.68l11.17-11.44c-8.04-12.29-18.81-22.6-31.48-30.15l-11.18,11.46c-5.72-3.07-11.8-5.43-18.1-7.05l-.27-15.97c-14.45-3.16-29.45-2.91-43.78,.74l.27,15.97c-6.24,1.83-12.23,4.4-17.84,7.66l-11.56-11.08c-12.41,7.97-22.81,18.64-30.44,31.19l11.56,11.06c-3.09,5.67-5.48,11.69-7.11,17.93l-16.13,.27Z" style="fill:#4cc2f1; fill-rule:evenodd; stroke:#fff; stroke-miterlimit:10;">
<animateTransform
attributeName="transform"
type="rotate"
values="0 261.91 113.28;-360 261.91 113.28"
dur="6s"
repeatCount="indefinite"/>
</path>
</g>
<!-- Left wheel center -->
<path d="M115.86,78.19c19.55,0,35.4,15.7,35.4,35.06s-15.85,35.06-35.4,35.06-35.4-15.7-35.4-35.06,15.85-35.06,35.4-35.06" style="fill:#f2b03f;"/>
<ellipse cx="116.42" cy="115.68" rx="20.17" ry="20.15" style="fill:none; stroke:#4159a4; stroke-miterlimit:10; stroke-width:1.58px;"/>
<path d="M116.42,107.88c4.35-.04,7.91,3.43,7.95,7.73,.04,4.31-3.46,7.83-7.81,7.87-4.35,.04-7.91-3.43-7.95-7.73,0-.02,0-.05,0-.07-.02-4.29,3.48-7.78,7.81-7.8" style="fill:#fff;"/>
<path d="M116.42,100.38c.2,0,.37,.16,.37,.36s-.16,.37-.37,.37c-.2,0-.37-.16-.37-.36,0,0,0,0,0,0,0-.2,.16-.36,.37-.37h0Zm1.38-4.53v-4.06h1.55c.82,.02,1.49-.63,1.51-1.43,.02-.81-.63-1.48-1.45-1.49-.02,0-.04,0-.06,0h-6.1c-.82-.02-1.49,.63-1.51,1.43-.02,.81,.63,1.48,1.45,1.49,.02,0,.04,0,.06,0h1.79v4.06m.14,18.68l6.92-9.15c.08-.11,.23-.13,.34-.05,.09,.07,.12,.19,.08,.29l-4.47,10.56c-.35,.84-1.32,1.23-2.16,.89-.07-.03-.14-.06-.2-.1-.8-.47-1.07-1.49-.6-2.28,.03-.05,.07-.11,.11-.16h0Zm-8.08-16.69l-1.86-3.97m2.09-.71l-3.98,1.86" style="fill:none; stroke:#4159a4; stroke-miterlimit:10; stroke-width:1.58px;"/>
<!-- Green dot with smooth pulse -->
<path d="M86.86,91.68c0-1.42,1.17-2.57,2.6-2.56,1.43,0,2.59,1.16,2.59,2.58,0,1.42-1.17,2.57-2.6,2.56-1.43,0-2.58-1.15-2.59-2.56v-.02Z" style="fill:#50a89c;">
<animateTransform attributeName="transform"
type="scale"
values="1;1.3;1"
dur="2s"
repeatCount="indefinite"/>
</path>
<!-- Connection elements forming infinity symbol - AGILE ANIMATION -->
<!-- Top left part of infinity -->
<path d="M225.8,83.92c1.22-1.35,2.46-2.66,3.75-3.94,16.69-16.54,43.22-18.12,61.79-3.68l17.46-2.65,2.57-16.63c-29.5-25.42-73.83-23.89-101.47,3.5-1.24,1.23-2.45,2.49-3.63,3.77l16.89,2.56,2.64,17.07Z" style="stroke:#fff; stroke-miterlimit:10;">
<animate attributeName="fill" values="#5e74b7;#00D4FF;#7B2BFF;#00FFB3;#5e74b7" dur="2s" repeatCount="indefinite"/>
<animate attributeName="stroke-width" values="1;3;1" dur="2s" repeatCount="indefinite"/>
</path>
<!-- Bottom left part of infinity -->
<path d="M78.59,141.18c-14.54-18.4-12.95-44.65,3.71-61.2,1.33-1.31,2.73-2.54,4.2-3.68l-17.46-2.65-2.57-16.63c-1.3,1.13-2.58,2.28-3.82,3.5-27.79,27.53-28.96,71.57-3.53,100.51l16.79-2.55,2.67-17.3Z" style="stroke:#fff; stroke-miterlimit:10;">
<animate attributeName="fill" values="#5e74b7;#7B2BFF;#00FFB3;#00D4FF;#5e74b7" dur="2s" begin="0.5s" repeatCount="indefinite"/>
<animate attributeName="stroke-width" values="1;3;1" dur="2s" begin="0.5s" repeatCount="indefinite"/>
</path>
<!-- Top right part of infinity -->
<path d="M86.5,76.3c18.57-14.44,45.1-12.86,61.79,3.68,1.29,1.27,2.53,2.6,3.75,3.94l2.64-17.07,16.89-2.56c-1.18-1.28-2.39-2.54-3.63-3.77-27.64-27.39-71.97-28.92-101.48-3.5l2.57,16.63,17.46,2.65Z" style="stroke:#fff; stroke-miterlimit:10;">
<animate attributeName="fill" values="#8d9ccf;#00FFB3;#00D4FF;#7B2BFF;#8d9ccf" dur="2s" begin="1s" repeatCount="indefinite"/>
<animate attributeName="stroke-width" values="1;3;1" dur="2s" begin="1s" repeatCount="indefinite"/>
</path>
<!-- Bottom right part of infinity -->
<path d="M299.26,141.18c-15.88,20.08-45.19,23.61-65.46,7.88l-.04-.03-17.46,2.65-2.57,16.63c29.5,25.42,73.83,23.89,101.47-3.5,1.24-1.22,2.4-2.49,3.53-3.78l-16.79-2.55-2.68-17.3Z" style="stroke:#fff; stroke-miterlimit:10;">
<animate attributeName="fill" values="#8d9ccf;#7B2BFF;#00D4FF;#00FFB3;#8d9ccf" dur="2s" begin="1.5s" repeatCount="indefinite"/>
<animate attributeName="stroke-width" values="1;3;1" dur="2s" begin="1.5s" repeatCount="indefinite"/>
</path>
<!-- Additional elements -->
<path d="M315.19,60.52c-1.23-1.22-2.51-2.38-3.82-3.5l-2.57,16.63-17.46,2.65c1.47,1.14,2.88,2.37,4.2,3.68,16.66,16.55,18.25,42.8,3.71,61.2l2.68,17.3,16.79,2.55c25.42-28.93,24.26-72.98-3.53-100.5m-171.1,88.5c-20.2,15.76-49.48,12.31-65.39-7.7-.04-.05-.08-.1-.12-.15l-2.68,17.3-16.79,2.55c1.13,1.29,2.3,2.56,3.53,3.78,27.64,27.39,71.97,28.92,101.47,3.5l-2.57-16.63-17.46-2.65Z" style="fill:#455aa5; stroke:#fff; stroke-miterlimit:10;"/>
<path d="M204.38,103.42c1.09,1.79,2.17,3.56,3.25,5.32,5.65-8.96,11.44-17.4,18.17-24.81l-2.64-17.07-16.89-2.56c-5.38,5.87-10.34,12.11-14.83,18.68,4.58,6.77,8.8,13.66,12.94,20.45m-30.92,18.5c-1.09-1.79-2.18-3.56-3.25-5.32-6.68,10.59-13.53,20.44-21.92,28.75-1.33,1.31-2.73,2.54-4.21,3.68l17.46,2.65,2.57,16.63c1.3-1.12,2.58-2.28,3.82-3.5,6.84-6.9,13.02-14.41,18.46-22.43-4.57-6.76-8.78-13.66-12.93-20.46" style="fill:#8d9ccf; stroke:#fff; stroke-miterlimit:10;"/>
<!-- Central flow representing infinity -->
<path d="M171.57,64.29c11.77,12.66,20.64,27.17,29.24,41.26,9,14.74,17.5,28.67,28.74,39.8,1.33,1.31,2.73,2.54,4.21,3.68l-17.46,2.65-2.57,16.63c-1.3-1.12-2.58-2.27-3.82-3.5-13.63-13.5-23.41-29.52-32.87-45.02-7.97-13.05-15.55-25.46-24.99-35.86l2.64-17.07,16.89-2.56Z" style="stroke:#fff; stroke-miterlimit:10;">
<animate attributeName="fill" values="#5e74b7;url(#infinityGradient);#00D4FF;url(#infinityGradient);#5e74b7" dur="3s" repeatCount="indefinite"/>
<animate attributeName="stroke-width" values="1;4;1" dur="3s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.8;1;0.8" dur="3s" repeatCount="indefinite"/>
</path>
<!-- Chronometer/clock with CSS pulse animation -->
<g>
<ellipse cx="261.91" cy="113.28" rx="35.65" ry="35.31" style="fill:#f9b224;">
<!-- CSS-style pulse animation: scale + opacity -->
<animateTransform attributeName="transform"
type="scale"
values="1;1.05;1"
dur="2s"
repeatCount="indefinite"/>
<animate attributeName="opacity"
values="1;0.75;1"
dur="2s"
repeatCount="indefinite"/>
</ellipse>
<!-- Chronometer display -->
<g style="clip-path:url(#d);">
<g>
<path d="M329.9,88.02v57.19c0,1.92-1.57,3.47-3.51,3.47h-89.87c-1.94,0-3.51-1.55-3.51-3.47v-57.19c0-1.92,1.57-3.47,3.51-3.47h89.87c1.94,0,3.51,1.55,3.51,3.47" style="fill:#5e74b7;"/>
<path d="M236.69,87.19c.3,0,.55,.24,.55,.54s-.25,.54-.55,.54-.55-.24-.55-.54,.25-.54,.55-.54h0" style="fill:#dba0a6;"/>
<path d="M238.63,87.19c.3,0,.55,.24,.55,.54s-.25,.54-.55,.54-.55-.24-.55-.54,.25-.54,.55-.54h0" style="fill:#f5cc56;"/>
<path d="M240.56,87.19c.3,0,.55,.24,.55,.54s-.25,.54-.55,.54-.55-.24-.55-.54,.25-.54,.55-.54h0" style="fill:#263682;"/>
<path d="M237.73,89.86h87.43c.92,0,1.66,.74,1.66,1.64s-.74,1.64-1.66,1.64h-87.43c-.92,0-1.66-.74-1.66-1.64s.74-1.64,1.66-1.64m-1.16,9.24h22.25v45.29h-22.25v-45.29Zm23.91,0h64.88v45.29h-64.88v-45.29Z" style="fill:#f3f4f5;"/>
<!-- Animated chart -->
<path d="M265.45,108.82v13.02h53.62v-9.61s-3.47,2.93-5.61,2.93c-3.49,0-5.5-2.32-7.99-2.27-3.52,.07-4.32,2.03-7.04,2.27-3.57,.31-4.73-4.21-9.35-4.47-4.62-.27-6.46,4.96-10.78,4.65-3.26-.23-6.17-5.71-8.31-6.51-1.48-.45-3.06-.45-4.53,0" style="fill:#f2ac41;">
<!-- Real-time data animation -->
<animateTransform attributeName="transform"
type="scale"
values="1,1;1.02,1.1;1,1"
dur="2s"
repeatCount="indefinite"/>
</path>
<path d="M239.78,102.82h4.96v4.93h-4.96v-4.93Zm0,7.89h4.96v4.93h-4.96v-4.93Zm0,7.89h4.96v4.93h-4.96v-4.93Zm0,7.89h4.96v4.93h-4.96v-4.93Zm0,7.89h4.96v4.93h-4.96v-4.93Z" style="fill:#8d9ccf;"/>
<path d="M284.39,127.69h16.01v11.29h-16.01v-11.29Zm-18.94,0h16.01v11.29h-16.01v-11.29Zm37.61,0h16.01v11.29h-16.01v-11.29Zm-56.48-22.8h9.27v1.08h-9.27v-1.08Zm0,7.89h9.27v1.08h-9.27v-1.08Zm0,7.7h9.27v1.08h-9.27v-1.08Zm0,7.89h9.27v1.08h-9.27v-1.08Zm0,7.79h9.27v1.08h-9.27v-1.08Z" style="fill:#d1d9e0;"/>
</g>
</g>
<!-- Green indicator with CSS pulse animation -->
<ellipse cx="287.99" cy="132.27" rx="11.69" ry="11.58" style="fill:#05ab9e;">
<animateTransform attributeName="transform"
type="scale"
values="1;1.05;1"
dur="2s"
repeatCount="indefinite"/>
<animate attributeName="opacity"
values="1;0.8;1"
dur="2s"
repeatCount="indefinite"/>
</ellipse>
<!-- Animated check mark -->
<path d="M281.89,131.18l4.78,4.73,8.02-7.94" style="fill:none; stroke:#fff; stroke-width:4px;">
<animate attributeName="stroke-dasharray"
values="0 20;20 20"
dur="1s"
repeatCount="indefinite"/>
</path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -0,0 +1,462 @@
<svg height="1080" viewBox="0 0 1920 1080" width="1920" xmlns="http://www.w3.org/2000/svg">
<style>
@keyframes wordAppear-group-0 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.8% {
opacity: 1;
transform: scale(1);
}
20.7% {
opacity: 1;
transform: scale(1);
}
34.7% {
opacity: 1;
transform: scale(1);
}
38.5% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-1 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.8% {
opacity: 1;
transform: scale(1);
}
20.7% {
opacity: 1;
transform: scale(1);
}
47.8% {
opacity: 1;
transform: scale(1);
}
51.5% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-2 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.8% {
opacity: 1;
transform: scale(1);
}
20.7% {
opacity: 1;
transform: scale(1);
}
60.8% {
opacity: 1;
transform: scale(1);
}
64.6% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-3 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.8% {
opacity: 1;
transform: scale(1);
}
20.7% {
opacity: 1;
transform: scale(1);
}
73.9% {
opacity: 1;
transform: scale(1);
}
77.6% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-4 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.8% {
opacity: 1;
transform: scale(1);
}
20.7% {
opacity: 1;
transform: scale(1);
}
86.9% {
opacity: 1;
transform: scale(1);
}
90.7% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
.word-anim {
animation-duration: 21.45s;
animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
animation-iteration-count: infinite;
animation-fill-mode: both;
transform-origin: center;
}
.word-0 { animation-delay: 0.00s; }
.word-1 { animation-delay: 0.05s; }
.word-2 { animation-delay: 0.10s; }
.word-3 { animation-delay: 0.15s; }
.word-4 { animation-delay: 0.20s; }
.word-5 { animation-delay: 0.25s; }
.word-6 { animation-delay: 0.30s; }
.word-7 { animation-delay: 0.35s; }
.word-8 { animation-delay: 0.40s; }
.word-9 { animation-delay: 0.45s; }
.word-10 { animation-delay: 0.50s; }
.word-11 { animation-delay: 0.55s; }
.word-12 { animation-delay: 0.60s; }
.word-13 { animation-delay: 0.65s; }
.word-14 { animation-delay: 0.70s; }
.word-15 { animation-delay: 0.75s; }
.word-16 { animation-delay: 0.80s; }
.word-17 { animation-delay: 0.85s; }
.word-18 { animation-delay: 0.90s; }
.word-19 { animation-delay: 0.95s; }
.word-20 { animation-delay: 1.00s; }
.word-21 { animation-delay: 1.05s; }
.word-22 { animation-delay: 1.10s; }
.word-23 { animation-delay: 1.15s; }
.word-24 { animation-delay: 1.20s; }
.word-25 { animation-delay: 1.25s; }
.word-26 { animation-delay: 1.30s; }
.word-27 { animation-delay: 1.35s; }
.word-28 { animation-delay: 1.40s; }
.word-29 { animation-delay: 1.45s; }
.word-30 { animation-delay: 1.50s; }
.word-31 { animation-delay: 1.55s; }
.word-32 { animation-delay: 1.60s; }
.word-33 { animation-delay: 1.65s; }
.word-34 { animation-delay: 1.70s; }
.word-35 { animation-delay: 1.75s; }
.word-36 { animation-delay: 1.80s; }
.word-37 { animation-delay: 1.85s; }
.word-38 { animation-delay: 1.90s; }
.word-39 { animation-delay: 1.95s; }
.word-40 { animation-delay: 2.00s; }
.word-41 { animation-delay: 2.05s; }
.word-42 { animation-delay: 2.10s; }
.word-43 { animation-delay: 2.15s; }
.word-44 { animation-delay: 2.20s; }
.word-45 { animation-delay: 2.25s; }
.word-46 { animation-delay: 2.30s; }
.word-47 { animation-delay: 2.35s; }
.word-48 { animation-delay: 2.40s; }
.word-49 { animation-delay: 2.45s; }
.word-50 { animation-delay: 2.50s; }
.word-51 { animation-delay: 2.55s; }
.word-52 { animation-delay: 2.60s; }
.word-53 { animation-delay: 2.65s; }
.word-54 { animation-delay: 2.70s; }
.word-55 { animation-delay: 2.75s; }
.word-56 { animation-delay: 2.80s; }
.word-57 { animation-delay: 2.85s; }
.word-58 { animation-delay: 2.90s; }
.word-59 { animation-delay: 2.95s; }
.word-60 { animation-delay: 3.00s; }
.word-61 { animation-delay: 3.05s; }
.word-62 { animation-delay: 3.10s; }
.word-63 { animation-delay: 3.15s; }
.word-64 { animation-delay: 3.20s; }
.word-65 { animation-delay: 3.25s; }
.word-66 { animation-delay: 3.30s; }
.word-67 { animation-delay: 3.35s; }
.word-68 { animation-delay: 3.40s; }
.word-69 { animation-delay: 3.45s; }
.word-70 { animation-delay: 3.50s; }
.word-71 { animation-delay: 3.55s; }
.word-72 { animation-delay: 3.60s; }
.word-73 { animation-delay: 3.65s; }
.word-74 { animation-delay: 3.70s; }
.word-75 { animation-delay: 3.75s; }
.word-76 { animation-delay: 3.80s; }
.group-0 { animation-name: wordAppear-group-0; }
.group-1 { animation-name: wordAppear-group-1; }
.group-2 { animation-name: wordAppear-group-2; }
.group-3 { animation-name: wordAppear-group-3; }
.group-4 { animation-name: wordAppear-group-4; }
</style>
<rect fill="#1a1a1a" height="100%" width="100%"/>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="120" font-weight="bold" x="823.2867" y="612.7063" class="word-anim word-76 group-4">
rust
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="68" font-weight="bold" x="834.5834" y="706.53235" class="word-anim word-75 group-3">
docker
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="50.666668" font-weight="bold" x="900.0582" y="447.55423" class="word-anim word-69 group-2">
git
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="50.666668" font-weight="bold" x="835.7112" y="375.89737" class="word-anim word-70 group-2">
upcloud
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="50.666668" font-weight="bold" x="1030.7035" y="450.67892" class="word-anim word-71 group-2">
aws
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="50.666668" font-weight="bold" x="777.27344" y="778.1361" class="word-anim word-72 group-2">
kubernetes
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="50.666668" font-weight="bold" x="650.9461" y="585.7749" class="word-anim word-73 group-2">
tokio
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="50.666668" font-weight="bold" x="687.56354" y="446.9147" class="word-anim word-74 group-2">
cargo
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="33.333336" font-weight="bold" x="622.3452" y="498.71942" class="word-anim word-55 group-1">
terraform
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="33.333336" font-weight="bold" x="709.7936" y="668.3757" class="word-anim word-56 group-1">
linux
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="33.333336" font-weight="bold" x="1142.7167" y="522.4315" class="word-anim word-57 group-1">
redis
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="33.333336" font-weight="bold" x="1111.0713" y="685.32666" class="word-anim word-58 group-1">
nushell
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="33.333336" font-weight="bold" x="1140.7495" y="615.9381" class="word-anim word-59 group-1">
web3
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="33.333336" font-weight="bold" x="909.4621" y="299.8963" class="word-anim word-60 group-1">
serde
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="33.333336" font-weight="bold" x="880.3608" y="830.3333" class="word-anim word-61 group-1">
polkadot
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="33.333336" font-weight="bold" x="1090.311" y="379.07935" class="word-anim word-62 group-1">
python
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="33.333336" font-weight="bold" x="661.66504" y="303.6575" class="word-anim word-63 group-1">
webassembly
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="33.333336" font-weight="bold" x="1137.9775" y="448.26636" class="word-anim word-64 group-1">
javascript
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="33.333336" font-weight="bold" x="1112.3065" y="749.97766" class="word-anim word-65 group-1">
axum
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="33.333336" font-weight="bold" x="611.6179" y="375.8344" class="word-anim word-66 group-1">
typescript
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="33.333336" font-weight="bold" x="841.6134" y="248.95023" class="word-anim word-67 group-1">
microservices
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="33.333336" font-weight="bold" x="892.8695" y="878.6886" class="word-anim word-68 group-1">
postgresql
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="736.7685" y="613.7388" class="word-anim word-0 group-0">
clippy
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1152.0361" y="561.1468" class="word-anim word-1 group-0">
consul
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="746.9528" y="701.1057" class="word-anim word-2 group-0">
shell
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="857.765" y="445.87625" class="word-anim word-3 group-0">
ssl
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="671.82825" y="616.7827" class="word-anim word-4 group-0">
vault
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1027.2041" y="299.13345" class="word-anim word-5 group-0">
vue.js
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="861.36847" y="409.65298" class="word-anim word-6 group-0">
api
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1073.9404" y="328.38574" class="word-anim word-7 group-0">
bash
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1113.3196" y="779.34924" class="word-anim word-8 group-0">
dynamodb
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="794.03754" y="807.1204" class="word-anim word-9 group-0">
alpine
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1221.2156" y="564.3587" class="word-anim word-10 group-0">
istio
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1063.6868" y="806.6036" class="word-anim word-11 group-0">
nomad
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="679.80023" y="732.4423" class="word-anim word-12 group-0">
jenkins
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="807.5205" y="697.3392" class="word-anim word-13 group-0">
vs
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1135.6116" y="325.8918" class="word-anim word-14 group-0">
tmux
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="664.3772" y="696.3824" class="word-anim word-15 group-0">
reqwest
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1237.0789" y="607.2373" class="word-anim word-16 group-0">
ansible
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="722.31415" y="758.94635" class="word-anim word-17 group-0">
code
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="654.5995" y="651.8692" class="word-anim word-18 group-0">
tls
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="833.013" y="838.08124" class="word-anim word-19 group-0">
grpc
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1116.3712" y="295.97476" class="word-anim word-20 group-0">
rest
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="691.3706" y="786.4342" class="word-anim word-21 group-0">
node.js
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1256.508" y="496.08237" class="word-anim word-22 group-0">
jwt
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1205.1747" y="726.18463" class="word-anim word-23 group-0">
nginx
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1249.336" y="635.66174" class="word-anim word-24 group-0">
s3
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="753.76196" y="837.1956" class="word-anim word-25 group-0">
graphql
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="859.0933" y="199.31187" class="word-anim word-26 group-0">
cloudformation
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="661.3085" y="436.88107" class="word-anim word-27 group-0">
go
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1265.1879" y="528.3801" class="word-anim word-28 group-0">
react
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1190.108" y="330.51392" class="word-anim word-29 group-0">
vim
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="635.4401" y="620.4401" class="word-anim word-30 group-0">
cli
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1227.298" y="389.54306" class="word-anim word-31 group-0">
oauth2
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1129.5402" y="816.4715" class="word-anim word-32 group-0">
helm
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="626.1987" y="408.15244" class="word-anim word-33 group-0">
hyper
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="779.9231" y="867.1834" class="word-anim word-34 group-0">
diesel
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="720.88904" y="248.24983" class="word-anim word-35 group-0">
serverless
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1131.1615" y="268.46768" class="word-anim word-36 group-0">
rocket
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1120.7202" y="843.8295" class="word-anim word-37 group-0">
ubuntu
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="569.90173" y="541.7937" class="word-anim word-38 group-0">
mongodb
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="601.0124" y="586.18634" class="word-anim word-39 group-0">
warp
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1175.5176" y="299.59494" class="word-anim word-40 group-0">
lambda
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="849.8949" y="865.67426" class="word-anim word-41 group-0">
rds
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1204.91" y="768.42755" class="word-anim word-42 group-0">
gitlab
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="900.36896" y="909.6198" class="word-anim word-43 group-0">
rustfmt
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1279.4791" y="576.0322" class="word-anim word-44 group-0">
packer
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="583.3478" y="436.60608" class="word-anim word-45 group-0">
grafana
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="986.3686" y="907.73065" class="word-anim word-46 group-0">
async-std
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="639.4653" y="758.48334" class="word-anim word-47 group-0">
rustup
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1277.0723" y="641.235" class="word-anim word-48 group-0">
ec2
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="781.97473" y="895.22406" class="word-anim word-49 group-0">
prometheus
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="574.8194" y="650.8389" class="word-anim word-50 group-0">
actions
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1269.1692" y="672.9548" class="word-anim word-51 group-0">
github
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1176.405" y="808.60144" class="word-anim word-52 group-0">
sqlx
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1009.946" y="197.27556" class="word-anim word-53 group-0">
actix-web
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="641.6829" y="818.75653" class="word-anim word-54 group-0">
cloudwatch
</text>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,762 @@
<svg height="1080" viewBox="0 0 1920 1080" width="1920" xmlns="http://www.w3.org/2000/svg">
<style>
@keyframes wordAppear-group-0 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.2% {
opacity: 1;
transform: scale(1);
}
28.4% {
opacity: 1;
transform: scale(1);
}
39.2% {
opacity: 1;
transform: scale(1);
}
42.1% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-1 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.2% {
opacity: 1;
transform: scale(1);
}
28.4% {
opacity: 1;
transform: scale(1);
}
49.4% {
opacity: 1;
transform: scale(1);
}
52.3% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-2 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.2% {
opacity: 1;
transform: scale(1);
}
28.4% {
opacity: 1;
transform: scale(1);
}
59.5% {
opacity: 1;
transform: scale(1);
}
62.4% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-3 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.2% {
opacity: 1;
transform: scale(1);
}
28.4% {
opacity: 1;
transform: scale(1);
}
69.6% {
opacity: 1;
transform: scale(1);
}
72.5% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-4 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.2% {
opacity: 1;
transform: scale(1);
}
28.4% {
opacity: 1;
transform: scale(1);
}
79.7% {
opacity: 1;
transform: scale(1);
}
82.6% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-5 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.2% {
opacity: 1;
transform: scale(1);
}
28.4% {
opacity: 1;
transform: scale(1);
}
89.9% {
opacity: 1;
transform: scale(1);
}
92.8% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
.word-anim {
animation-duration: 27.65s;
animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
animation-iteration-count: infinite;
animation-fill-mode: both;
transform-origin: center;
}
.word-0 { animation-delay: 0.00s; }
.word-1 { animation-delay: 0.05s; }
.word-2 { animation-delay: 0.10s; }
.word-3 { animation-delay: 0.15s; }
.word-4 { animation-delay: 0.20s; }
.word-5 { animation-delay: 0.25s; }
.word-6 { animation-delay: 0.30s; }
.word-7 { animation-delay: 0.35s; }
.word-8 { animation-delay: 0.40s; }
.word-9 { animation-delay: 0.45s; }
.word-10 { animation-delay: 0.50s; }
.word-11 { animation-delay: 0.55s; }
.word-12 { animation-delay: 0.60s; }
.word-13 { animation-delay: 0.65s; }
.word-14 { animation-delay: 0.70s; }
.word-15 { animation-delay: 0.75s; }
.word-16 { animation-delay: 0.80s; }
.word-17 { animation-delay: 0.85s; }
.word-18 { animation-delay: 0.90s; }
.word-19 { animation-delay: 0.95s; }
.word-20 { animation-delay: 1.00s; }
.word-21 { animation-delay: 1.05s; }
.word-22 { animation-delay: 1.10s; }
.word-23 { animation-delay: 1.15s; }
.word-24 { animation-delay: 1.20s; }
.word-25 { animation-delay: 1.25s; }
.word-26 { animation-delay: 1.30s; }
.word-27 { animation-delay: 1.35s; }
.word-28 { animation-delay: 1.40s; }
.word-29 { animation-delay: 1.45s; }
.word-30 { animation-delay: 1.50s; }
.word-31 { animation-delay: 1.55s; }
.word-32 { animation-delay: 1.60s; }
.word-33 { animation-delay: 1.65s; }
.word-34 { animation-delay: 1.70s; }
.word-35 { animation-delay: 1.75s; }
.word-36 { animation-delay: 1.80s; }
.word-37 { animation-delay: 1.85s; }
.word-38 { animation-delay: 1.90s; }
.word-39 { animation-delay: 1.95s; }
.word-40 { animation-delay: 2.00s; }
.word-41 { animation-delay: 2.05s; }
.word-42 { animation-delay: 2.10s; }
.word-43 { animation-delay: 2.15s; }
.word-44 { animation-delay: 2.20s; }
.word-45 { animation-delay: 2.25s; }
.word-46 { animation-delay: 2.30s; }
.word-47 { animation-delay: 2.35s; }
.word-48 { animation-delay: 2.40s; }
.word-49 { animation-delay: 2.45s; }
.word-50 { animation-delay: 2.50s; }
.word-51 { animation-delay: 2.55s; }
.word-52 { animation-delay: 2.60s; }
.word-53 { animation-delay: 2.65s; }
.word-54 { animation-delay: 2.70s; }
.word-55 { animation-delay: 2.75s; }
.word-56 { animation-delay: 2.80s; }
.word-57 { animation-delay: 2.85s; }
.word-58 { animation-delay: 2.90s; }
.word-59 { animation-delay: 2.95s; }
.word-60 { animation-delay: 3.00s; }
.word-61 { animation-delay: 3.05s; }
.word-62 { animation-delay: 3.10s; }
.word-63 { animation-delay: 3.15s; }
.word-64 { animation-delay: 3.20s; }
.word-65 { animation-delay: 3.25s; }
.word-66 { animation-delay: 3.30s; }
.word-67 { animation-delay: 3.35s; }
.word-68 { animation-delay: 3.40s; }
.word-69 { animation-delay: 3.45s; }
.word-70 { animation-delay: 3.50s; }
.word-71 { animation-delay: 3.55s; }
.word-72 { animation-delay: 3.60s; }
.word-73 { animation-delay: 3.65s; }
.word-74 { animation-delay: 3.70s; }
.word-75 { animation-delay: 3.75s; }
.word-76 { animation-delay: 3.80s; }
.word-77 { animation-delay: 3.85s; }
.word-78 { animation-delay: 3.90s; }
.word-79 { animation-delay: 3.95s; }
.word-80 { animation-delay: 4.00s; }
.word-81 { animation-delay: 4.05s; }
.word-82 { animation-delay: 4.10s; }
.word-83 { animation-delay: 4.15s; }
.word-84 { animation-delay: 4.20s; }
.word-85 { animation-delay: 4.25s; }
.word-86 { animation-delay: 4.30s; }
.word-87 { animation-delay: 4.35s; }
.word-88 { animation-delay: 4.40s; }
.word-89 { animation-delay: 4.45s; }
.word-90 { animation-delay: 4.50s; }
.word-91 { animation-delay: 4.55s; }
.word-92 { animation-delay: 4.60s; }
.word-93 { animation-delay: 4.65s; }
.word-94 { animation-delay: 4.70s; }
.word-95 { animation-delay: 4.75s; }
.word-96 { animation-delay: 4.80s; }
.word-97 { animation-delay: 4.85s; }
.word-98 { animation-delay: 4.90s; }
.word-99 { animation-delay: 4.95s; }
.word-100 { animation-delay: 5.00s; }
.word-101 { animation-delay: 5.05s; }
.word-102 { animation-delay: 5.10s; }
.word-103 { animation-delay: 5.15s; }
.word-104 { animation-delay: 5.20s; }
.word-105 { animation-delay: 5.25s; }
.word-106 { animation-delay: 5.30s; }
.word-107 { animation-delay: 5.35s; }
.word-108 { animation-delay: 5.40s; }
.word-109 { animation-delay: 5.45s; }
.word-110 { animation-delay: 5.50s; }
.word-111 { animation-delay: 5.55s; }
.word-112 { animation-delay: 5.60s; }
.word-113 { animation-delay: 5.65s; }
.word-114 { animation-delay: 5.70s; }
.word-115 { animation-delay: 5.75s; }
.word-116 { animation-delay: 5.80s; }
.word-117 { animation-delay: 5.85s; }
.word-118 { animation-delay: 5.90s; }
.word-119 { animation-delay: 5.95s; }
.word-120 { animation-delay: 6.00s; }
.word-121 { animation-delay: 6.05s; }
.word-122 { animation-delay: 6.10s; }
.word-123 { animation-delay: 6.15s; }
.word-124 { animation-delay: 6.20s; }
.word-125 { animation-delay: 6.25s; }
.word-126 { animation-delay: 6.30s; }
.word-127 { animation-delay: 6.35s; }
.word-128 { animation-delay: 6.40s; }
.word-129 { animation-delay: 6.45s; }
.word-130 { animation-delay: 6.50s; }
.word-131 { animation-delay: 6.55s; }
.word-132 { animation-delay: 6.60s; }
.word-133 { animation-delay: 6.65s; }
.word-134 { animation-delay: 6.70s; }
.word-135 { animation-delay: 6.75s; }
.word-136 { animation-delay: 6.80s; }
.word-137 { animation-delay: 6.85s; }
.word-138 { animation-delay: 6.90s; }
.word-139 { animation-delay: 6.95s; }
.word-140 { animation-delay: 7.00s; }
.word-141 { animation-delay: 7.05s; }
.word-142 { animation-delay: 7.10s; }
.word-143 { animation-delay: 7.15s; }
.word-144 { animation-delay: 7.20s; }
.group-0 { animation-name: wordAppear-group-0; }
.group-1 { animation-name: wordAppear-group-1; }
.group-2 { animation-name: wordAppear-group-2; }
.group-3 { animation-name: wordAppear-group-3; }
.group-4 { animation-name: wordAppear-group-4; }
.group-5 { animation-name: wordAppear-group-5; }
</style>
<rect fill="#1a1a1a" height="100%" width="100%"/>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="120" font-weight="bold" x="748.48004" y="606.72656" class="word-anim word-144 group-5">
cargo
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="99.200005" font-weight="bold" x="805.598" y="743.39667" class="word-anim word-142 group-4">
tokio
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="99.200005" font-weight="bold" x="784.775" y="442.52783" class="word-anim word-143 group-4">
serde
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="78.4" font-weight="bold" x="812.89667" y="851.051" class="word-anim word-138 group-3">
clippy
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="78.4" font-weight="bold" x="776.38403" y="305.3883" class="word-anim word-139 group-3">
rustfmt
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="78.4" font-weight="bold" x="1145.3862" y="541.0996" class="word-anim word-140 group-3">
rustup
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="78.4" font-weight="bold" x="1146.9691" y="649.55444" class="word-anim word-141 group-3">
axum
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="682.22675" y="693.83954" class="word-anim word-118 group-2">
log
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="1134.9784" y="427.37225" class="word-anim word-119 group-2">
rocket
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="553.2637" y="551.8116" class="word-anim word-120 group-2">
regex
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="629.7401" y="416.52435" class="word-anim word-121 group-2">
bevy
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="1133.142" y="734.7104" class="word-anim word-122 group-2">
tracing
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="636.43494" y="775.2529" class="word-anim word-123 group-2">
sqlx
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="780.3532" y="932.43024" class="word-anim word-124 group-2">
thiserror
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="579.66187" y="332.2915" class="word-anim word-125 group-2">
tauri
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="823.4691" y="195.35751" class="word-anim word-126 group-2">
actix-web
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="1144.7175" y="344.80664" class="word-anim word-127 group-2">
yew
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="1133.4017" y="834.9039" class="word-anim word-128 group-2">
diesel
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="783.50684" y="1013.3035" class="word-anim word-129 group-2">
crates.io
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="524.4424" y="637.89984" class="word-anim word-130 group-2">
warp
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="505.11945" y="865.99725" class="word-anim word-131 group-2">
reqwest
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="1126.9934" y="916.68304" class="word-anim word-132 group-2">
clap
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="657.87054" y="114.58154" class="word-anim word-133 group-2">
rust-analyzer
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="577.0718" y="248.70169" class="word-anim word-134 group-2">
rayon
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="442.9711" y="732.8759" class="word-anim word-135 group-2">
hyper
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="1168.3464" y="261.3575" class="word-anim word-136 group-2">
anyhow
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="57.600002" font-weight="bold" x="397.57968" y="430.3089" class="word-anim word-137 group-2">
chrono
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1289.8667" y="342.67868" class="word-anim word-57 group-1">
dioxus
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="664.64124" y="920.0086" class="word-anim word-58 group-1">
pest
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="804.90936" y="1068.6559" class="word-anim word-59 group-1">
parking_lot
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1357.368" y="639.448" class="word-anim word-60 group-1">
eyre
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="609.62054" y="167.8974" class="word-anim word-61 group-1">
mongodb
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="402.76398" y="537.66583" class="word-anim word-62 group-1">
tui-rs
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1140.6888" y="979.2502" class="word-anim word-63 group-1">
sea-orm
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="536.3336" y="985.1112" class="word-anim word-64 group-1">
once_cell
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1189.6624" y="169.91833" class="word-anim word-65 group-1">
flexi_logger
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1367.1548" y="419.5213" class="word-anim word-66 group-1">
mysql
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="449.86365" y="317.30627" class="word-anim word-67 group-1">
cross
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1131.1317" y="1033.5532" class="word-anim word-68 group-1">
docs.rs
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1155.9713" y="104.69432" class="word-anim word-69 group-1">
quickcheck
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1283.5242" y="905.4491" class="word-anim word-70 group-1">
crossbeam
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="449.2697" y="923.6467" class="word-anim word-71 group-1">
rusqlite
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="415.2076" y="785.72675" class="word-anim word-72 group-1">
shuttle
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="400.9742" y="607.53015" class="word-anim word-73 group-1">
ring
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1407.105" y="701.50336" class="word-anim word-74 group-1">
cargo-watch
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1367.2986" y="817.4883" class="word-anim word-75 group-1">
openssl
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="554.60114" y="1044.7194" class="word-anim word-76 group-1">
postgres
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1400.953" y="761.21875" class="word-anim word-77 group-1">
wasm-bindgen
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="668.76434" y="470.45667" class="word-anim word-78 group-1">
ron
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1462.9099" y="567.47034" class="word-anim word-79 group-1">
rustdoc
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="317.21033" y="483.28357" class="word-anim word-80 group-1">
rustls
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="329.21188" y="255.95996" class="word-anim word-81 group-1">
color-eyre
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1401.0704" y="278.84067" class="word-anim word-82 group-1">
toml
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1471.1332" y="504.01114" class="word-anim word-83 group-1">
wgpu
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1464.3763" y="637.94293" class="word-anim word-84 group-1">
leptos
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="556.1537" y="114.5339" class="word-anim word-85 group-1">
smol
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1440.4441" y="358.68665" class="word-anim word-86 group-1">
sha2
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="377.70258" y="175.15683" class="word-anim word-87 group-1">
proptest
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1327.5178" y="971.92145" class="word-anim word-88 group-1">
bacon
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="345.21783" y="661.3189" class="word-anim word-89 group-1">
nom
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="234.59329" y="845.84766" class="word-anim word-90 group-1">
env_logger
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1495.1613" y="433.6325" class="word-anim word-91 group-1">
time
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="307.02966" y="732.5587" class="word-anim word-92 group-1">
trunk
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1311.052" y="1037.4224" class="word-anim word-93 group-1">
structopt
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="178.0594" y="608.21545" class="word-anim word-94 group-1">
indicatif
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="341.01523" y="317.9748" class="word-anim word-95 group-1">
slog
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="424.89728" y="979.7019" class="word-anim word-96 group-1">
miri
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="271.11826" y="552.5245" class="word-anim word-97 group-1">
uuid
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1414.1011" y="223.88007" class="word-anim word-98 group-1">
wasmer
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="292.467" y="418.31982" class="word-anim word-99 group-1">
iced
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="473.87708" y="61.18944" class="word-anim word-100 group-1">
blake3
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="350.9781" y="1046.5547" class="word-anim word-101 group-1">
redis-rs
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="164.51157" y="786.3839" class="word-anim word-102 group-1">
native-tls
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="375.53638" y="121.79449" class="word-anim word-103 group-1">
dashmap
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1505.9111" y="284.4144" class="word-anim word-104 group-1">
web-sys
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1551.5951" y="369.0549" class="word-anim word-105 group-1">
criterion
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="271.04276" y="916.8417" class="word-anim word-106 group-1">
console
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1408.1805" y="107.34691" class="word-anim word-107 group-1">
egui
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1574.1619" y="493.97296" class="word-anim word-108 group-1">
mdbook
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1507.0905" y="875.05664" class="word-anim word-109 group-1">
rand
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1565.4562" y="818.0524" class="word-anim word-110 group-1">
wasmtime
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1470.179" y="983.3951" class="word-anim word-111 group-1">
async-std
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="97.854996" y="673.9128" class="word-anim word-112 group-1">
wasm-pack
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1499.0867" y="167.59943" class="word-anim word-113 group-1">
ratatui
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="216.45877" y="493.8029" class="word-anim word-114 group-1">
tide
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="236.70181" y="364.79218" class="word-anim word-115 group-1">
argh
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1615.7786" y="439.6123" class="word-anim word-116 group-1">
lazy_static
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="36.800003" font-weight="bold" x="1620.0417" y="634.8295" class="word-anim word-117 group-1">
js-sys
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="520.1648" y="461.1654" class="word-anim word-0 group-0">
cargo-edit
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="704.7793" y="581.2503" class="word-anim word-1 group-0">
ash
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1375.7961" y="575.22516" class="word-anim word-2 group-0">
aes-gcm
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1036.5228" y="33.28257" class="word-anim word-3 group-0">
cargo-make
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1280.7123" y="289.36783" class="word-anim word-4 group-0">
vercel
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="444.0668" y="348.417" class="word-anim word-5 group-0">
cargo-tree
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="444.19882" y="635.50433" class="word-anim word-6 group-0">
railway
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="924.162" y="34.37571" class="word-anim word-7 group-0">
cranelift
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="842.5741" y="30.993383" class="word-anim word-8 group-0">
vulkano
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1119.2593" y="1068.6913" class="word-anim word-9 group-0">
massif-visualizer
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="750.9245" y="32.657143" class="word-anim word-10 group-0">
gfx-hal
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1196.3647" y="51.26008" class="word-anim word-11 group-0">
ggez
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="637.921" y="33.66514" class="word-anim word-12 group-0">
valgrind
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="689.45337" y="607.60284" class="word-anim word-13 group-0">
lucet
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1363.6497" y="846.3438" class="word-anim word-14 group-0">
percy
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1266.177" y="38.942635" class="word-anim word-15 group-0">
seed
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="348.56735" y="349.7534" class="word-anim word-16 group-0">
bincode
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="589.75586" y="774.4051" class="word-anim word-17 group-0">
ureq
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1317.9447" y="37.15055" class="word-anim word-18 group-0">
cargo-release
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="471.61548" y="471.6832" class="word-anim word-19 group-0">
surf
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="476.6994" y="202.61324" class="word-anim word-20 group-0">
yaml-rust
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1421.2067" y="846.805" class="word-anim word-21 group-0">
cursive
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1321.9318" y="1067.411" class="word-anim word-22 group-0">
cargo-workspaces
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="342.63492" y="201.0312" class="word-anim word-23 group-0">
cargo-expand
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="273.93625" y="944.4953" class="word-anim word-24 group-0">
ed25519-dalek
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1512.8723" y="911.43945" class="word-anim word-25 group-0">
pprof
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1578.5135" y="315.4192" class="word-anim word-26 group-0">
druid
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="352.15942" y="54.96845" class="word-anim word-27 group-0">
cargo-audit
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="188.60565" y="295.9269" class="word-anim word-28 group-0">
cargo-outdated
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1568.7882" y="228.70811" class="word-anim word-29 group-0">
bpftrace
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="174.4466" y="426.78067" class="word-anim word-30 group-0">
flamegraph
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="242.65057" y="704.38837" class="word-anim word-31 group-0">
logos
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1639.876" y="550.3328" class="word-anim word-32 group-0">
fly.io
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="170.9199" y="544.2677" class="word-anim word-33 group-0">
heaptrack
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="305.9359" y="972.4439" class="word-anim word-34 group-0">
macroquad
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1564.8086" y="193.55373" class="word-anim word-35 group-0">
perf
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="314.21002" y="166.26503" class="word-anim word-36 group-0">
divan
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1646.9497" y="576.8006" class="word-anim word-37 group-0">
render
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="256.9168" y="259.48334" class="word-anim word-38 group-0">
glutin
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1508.9402" y="106.36261" class="word-anim word-39 group-0">
argon2
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1515.9661" y="80.06494" class="word-anim word-40 group-0">
console_error_panic_hook
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1575.3351" y="901.5401" class="word-anim word-41 group-0">
postcard
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1643.892" y="523.39105" class="word-anim word-42 group-0">
amethyst
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="142.76126" y="397.5779" class="word-anim word-43 group-0">
cargo-geiger
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="138.08478" y="733.41754" class="word-anim word-44 group-0">
prettytable-rs
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1610.0635" y="846.659" class="word-anim word-45 group-0">
curl-rust
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1471.6113" y="51.679756" class="word-anim word-46 group-0">
dtrace
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="168.17514" y="122.16635" class="word-anim word-47 group-0">
cloudflare-workers
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="229.6637" y="196.76653" class="word-anim word-48 group-0">
cargo-deny
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="72.4697" y="227.1043" class="word-anim word-49 group-0">
aws-lambda-rust-runtime
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1654.518" y="313.45975" class="word-anim word-50 group-0">
lalrpop
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1616.2471" y="202.36858" class="word-anim word-51 group-0">
getrandom
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1541.2716" y="1009.61676" class="word-anim word-52 group-0">
cargo-bloat
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1683.7064" y="678.30884" class="word-anim word-53 group-0">
fastrand
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="171.60939" y="705.6277" class="word-anim word-54 group-0">
piston
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="312.66223" y="92.99191" class="word-anim word-55 group-0">
winit
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="214.28067" y="998.4201" class="word-anim word-56 group-0">
cargo-readme
</text>
</svg>

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -0,0 +1,714 @@
<svg height="1080" viewBox="0 0 1920 1080" width="1920" xmlns="http://www.w3.org/2000/svg">
<style>
@keyframes wordAppear-group-0 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.2% {
opacity: 1;
transform: scale(1);
}
26.8% {
opacity: 1;
transform: scale(1);
}
37.9% {
opacity: 1;
transform: scale(1);
}
40.9% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-1 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.2% {
opacity: 1;
transform: scale(1);
}
26.8% {
opacity: 1;
transform: scale(1);
}
48.2% {
opacity: 1;
transform: scale(1);
}
51.2% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-2 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.2% {
opacity: 1;
transform: scale(1);
}
26.8% {
opacity: 1;
transform: scale(1);
}
58.6% {
opacity: 1;
transform: scale(1);
}
61.6% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-3 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.2% {
opacity: 1;
transform: scale(1);
}
26.8% {
opacity: 1;
transform: scale(1);
}
68.9% {
opacity: 1;
transform: scale(1);
}
71.9% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-4 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.2% {
opacity: 1;
transform: scale(1);
}
26.8% {
opacity: 1;
transform: scale(1);
}
79.3% {
opacity: 1;
transform: scale(1);
}
82.3% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-5 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.2% {
opacity: 1;
transform: scale(1);
}
26.8% {
opacity: 1;
transform: scale(1);
}
89.6% {
opacity: 1;
transform: scale(1);
}
92.6% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
.word-anim {
animation-duration: 27.05s;
animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
animation-iteration-count: infinite;
animation-fill-mode: both;
transform-origin: center;
}
.word-0 { animation-delay: 0.00s; }
.word-1 { animation-delay: 0.05s; }
.word-2 { animation-delay: 0.10s; }
.word-3 { animation-delay: 0.15s; }
.word-4 { animation-delay: 0.20s; }
.word-5 { animation-delay: 0.25s; }
.word-6 { animation-delay: 0.30s; }
.word-7 { animation-delay: 0.35s; }
.word-8 { animation-delay: 0.40s; }
.word-9 { animation-delay: 0.45s; }
.word-10 { animation-delay: 0.50s; }
.word-11 { animation-delay: 0.55s; }
.word-12 { animation-delay: 0.60s; }
.word-13 { animation-delay: 0.65s; }
.word-14 { animation-delay: 0.70s; }
.word-15 { animation-delay: 0.75s; }
.word-16 { animation-delay: 0.80s; }
.word-17 { animation-delay: 0.85s; }
.word-18 { animation-delay: 0.90s; }
.word-19 { animation-delay: 0.95s; }
.word-20 { animation-delay: 1.00s; }
.word-21 { animation-delay: 1.05s; }
.word-22 { animation-delay: 1.10s; }
.word-23 { animation-delay: 1.15s; }
.word-24 { animation-delay: 1.20s; }
.word-25 { animation-delay: 1.25s; }
.word-26 { animation-delay: 1.30s; }
.word-27 { animation-delay: 1.35s; }
.word-28 { animation-delay: 1.40s; }
.word-29 { animation-delay: 1.45s; }
.word-30 { animation-delay: 1.50s; }
.word-31 { animation-delay: 1.55s; }
.word-32 { animation-delay: 1.60s; }
.word-33 { animation-delay: 1.65s; }
.word-34 { animation-delay: 1.70s; }
.word-35 { animation-delay: 1.75s; }
.word-36 { animation-delay: 1.80s; }
.word-37 { animation-delay: 1.85s; }
.word-38 { animation-delay: 1.90s; }
.word-39 { animation-delay: 1.95s; }
.word-40 { animation-delay: 2.00s; }
.word-41 { animation-delay: 2.05s; }
.word-42 { animation-delay: 2.10s; }
.word-43 { animation-delay: 2.15s; }
.word-44 { animation-delay: 2.20s; }
.word-45 { animation-delay: 2.25s; }
.word-46 { animation-delay: 2.30s; }
.word-47 { animation-delay: 2.35s; }
.word-48 { animation-delay: 2.40s; }
.word-49 { animation-delay: 2.45s; }
.word-50 { animation-delay: 2.50s; }
.word-51 { animation-delay: 2.55s; }
.word-52 { animation-delay: 2.60s; }
.word-53 { animation-delay: 2.65s; }
.word-54 { animation-delay: 2.70s; }
.word-55 { animation-delay: 2.75s; }
.word-56 { animation-delay: 2.80s; }
.word-57 { animation-delay: 2.85s; }
.word-58 { animation-delay: 2.90s; }
.word-59 { animation-delay: 2.95s; }
.word-60 { animation-delay: 3.00s; }
.word-61 { animation-delay: 3.05s; }
.word-62 { animation-delay: 3.10s; }
.word-63 { animation-delay: 3.15s; }
.word-64 { animation-delay: 3.20s; }
.word-65 { animation-delay: 3.25s; }
.word-66 { animation-delay: 3.30s; }
.word-67 { animation-delay: 3.35s; }
.word-68 { animation-delay: 3.40s; }
.word-69 { animation-delay: 3.45s; }
.word-70 { animation-delay: 3.50s; }
.word-71 { animation-delay: 3.55s; }
.word-72 { animation-delay: 3.60s; }
.word-73 { animation-delay: 3.65s; }
.word-74 { animation-delay: 3.70s; }
.word-75 { animation-delay: 3.75s; }
.word-76 { animation-delay: 3.80s; }
.word-77 { animation-delay: 3.85s; }
.word-78 { animation-delay: 3.90s; }
.word-79 { animation-delay: 3.95s; }
.word-80 { animation-delay: 4.00s; }
.word-81 { animation-delay: 4.05s; }
.word-82 { animation-delay: 4.10s; }
.word-83 { animation-delay: 4.15s; }
.word-84 { animation-delay: 4.20s; }
.word-85 { animation-delay: 4.25s; }
.word-86 { animation-delay: 4.30s; }
.word-87 { animation-delay: 4.35s; }
.word-88 { animation-delay: 4.40s; }
.word-89 { animation-delay: 4.45s; }
.word-90 { animation-delay: 4.50s; }
.word-91 { animation-delay: 4.55s; }
.word-92 { animation-delay: 4.60s; }
.word-93 { animation-delay: 4.65s; }
.word-94 { animation-delay: 4.70s; }
.word-95 { animation-delay: 4.75s; }
.word-96 { animation-delay: 4.80s; }
.word-97 { animation-delay: 4.85s; }
.word-98 { animation-delay: 4.90s; }
.word-99 { animation-delay: 4.95s; }
.word-100 { animation-delay: 5.00s; }
.word-101 { animation-delay: 5.05s; }
.word-102 { animation-delay: 5.10s; }
.word-103 { animation-delay: 5.15s; }
.word-104 { animation-delay: 5.20s; }
.word-105 { animation-delay: 5.25s; }
.word-106 { animation-delay: 5.30s; }
.word-107 { animation-delay: 5.35s; }
.word-108 { animation-delay: 5.40s; }
.word-109 { animation-delay: 5.45s; }
.word-110 { animation-delay: 5.50s; }
.word-111 { animation-delay: 5.55s; }
.word-112 { animation-delay: 5.60s; }
.word-113 { animation-delay: 5.65s; }
.word-114 { animation-delay: 5.70s; }
.word-115 { animation-delay: 5.75s; }
.word-116 { animation-delay: 5.80s; }
.word-117 { animation-delay: 5.85s; }
.word-118 { animation-delay: 5.90s; }
.word-119 { animation-delay: 5.95s; }
.word-120 { animation-delay: 6.00s; }
.word-121 { animation-delay: 6.05s; }
.word-122 { animation-delay: 6.10s; }
.word-123 { animation-delay: 6.15s; }
.word-124 { animation-delay: 6.20s; }
.word-125 { animation-delay: 6.25s; }
.word-126 { animation-delay: 6.30s; }
.word-127 { animation-delay: 6.35s; }
.word-128 { animation-delay: 6.40s; }
.word-129 { animation-delay: 6.45s; }
.word-130 { animation-delay: 6.50s; }
.word-131 { animation-delay: 6.55s; }
.word-132 { animation-delay: 6.60s; }
.group-0 { animation-name: wordAppear-group-0; }
.group-1 { animation-name: wordAppear-group-1; }
.group-2 { animation-name: wordAppear-group-2; }
.group-3 { animation-name: wordAppear-group-3; }
.group-4 { animation-name: wordAppear-group-4; }
.group-5 { animation-name: wordAppear-group-5; }
</style>
<rect fill="#1a1a1a" height="100%" width="100%"/>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="120" font-weight="bold" x="735.2111" y="635.7526" class="word-anim word-132 group-5">
safety
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="108.44444" font-weight="bold" x="793.40186" y="473.43298" class="word-anim word-131 group-5">
type
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="62.22222" font-weight="bold" x="825.2026" y="722.8755" class="word-anim word-126 group-4">
memory
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="62.22222" font-weight="bold" x="784.7082" y="813.3367" class="word-anim word-127 group-4">
ownership
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="62.22222" font-weight="bold" x="698.1675" y="325.815" class="word-anim word-128 group-4">
concurrency
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="62.22222" font-weight="bold" x="1095.7632" y="473.7722" class="word-anim word-129 group-4">
macros
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="62.22222" font-weight="bold" x="775.34094" y="900.136" class="word-anim word-130 group-4">
performance
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="50.666668" font-weight="bold" x="1078.2418" y="722.7665" class="word-anim word-119 group-3">
error
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="50.666668" font-weight="bold" x="636.30756" y="708.8938" class="word-anim word-120 group-3">
async
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="50.666668" font-weight="bold" x="586.0368" y="439.3702" class="word-anim word-121 group-3">
traits
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="50.666668" font-weight="bold" x="816.3589" y="238.02782" class="word-anim word-122 group-3">
pattern
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="50.666668" font-weight="bold" x="433.35394" y="610.14307" class="word-anim word-123 group-3">
zero-cost
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="50.666668" font-weight="bold" x="432.82977" y="519.9343" class="word-anim word-124 group-3">
borrowing
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="50.666668" font-weight="bold" x="1148.442" y="377.83658" class="word-anim word-125 group-3">
without
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="39.11111" font-weight="bold" x="1209.1445" y="539.7714" class="word-anim word-105 group-2">
borrow
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="39.11111" font-weight="bold" x="1212.9713" y="596.6636" class="word-anim word-106 group-2">
types
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="39.11111" font-weight="bold" x="618.9387" y="775.9134" class="word-anim word-107 group-2">
enums
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="39.11111" font-weight="bold" x="1161.2539" y="788.8165" class="word-anim word-108 group-2">
system
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="39.11111" font-weight="bold" x="861.6464" y="957.2812" class="word-anim word-109 group-2">
generics
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="39.11111" font-weight="bold" x="903.4875" y="166.54991" class="word-anim word-110 group-2">
fearless
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="39.11111" font-weight="bold" x="1051.9758" y="230.9412" class="word-anim word-111 group-2">
matching
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="39.11111" font-weight="bold" x="1169.3943" y="295.03787" class="word-anim word-112 group-2">
handling
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="39.11111" font-weight="bold" x="585.51917" y="836.45557" class="word-anim word-113 group-2">
checker
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="39.11111" font-weight="bold" x="1252.2213" y="677.3689" class="word-anim word-114 group-2">
await
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="39.11111" font-weight="bold" x="556.16675" y="356.61853" class="word-anim word-115 group-2">
cargo
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="39.11111" font-weight="bold" x="787.91345" y="1016.9533" class="word-anim word-116 group-2">
abstractions
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="39.11111" font-weight="bold" x="605.53204" y="235.09915" class="word-anim word-117 group-2">
compiler
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="39.11111" font-weight="bold" x="663.7373" y="156.02272" class="word-anim word-118 group-2">
lifetimes
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="1276.8109" y="726.2692" class="word-anim word-76 group-1">
pointers
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="535.66876" y="660.7286" class="word-anim word-77 group-1">
raii
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="722.56085" y="942.4408" class="word-anim word-78 group-1">
futures
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="624.2809" y="885.76904" class="word-anim word-79 group-1">
closures
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="1081.3606" y="943.7568" class="word-anim word-80 group-1">
inference
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="460.80283" y="714.2667" class="word-anim word-81 group-1">
iterators
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="923.56036" y="109.86048" class="word-anim word-82 group-1">
cross-platform
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="618.96484" y="281.41534" class="word-anim word-83 group-1">
move
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="1234.7047" y="832.1577" class="word-anim word-84 group-1">
null-free
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="1089.5236" y="381.48126" class="word-anim word-85 group-1">
ffi
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="1137.4182" y="169.08006" class="word-anim word-86 group-1">
trait
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="1346.8054" y="601.6777" class="word-anim word-87 group-1">
garbage
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="1344.3141" y="443.9552" class="word-anim word-88 group-1">
semantics
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="1228.0975" y="890.47473" class="word-anim word-89 group-1">
community
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="498.5083" y="765.56494" class="word-anim word-90 group-1">
option
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="470.35883" y="446.06335" class="word-anim word-91 group-1">
thread
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="1370.3688" y="516.16046" class="word-anim word-92 group-1">
support
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="728.1983" y="86.50129" class="word-anim word-93 group-1">
mutability
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="577.5595" y="276.73132" class="word-anim word-94 group-1">
by
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="588.45966" y="929.8461" class="word-anim word-95 group-1">
default
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="1386.1315" y="560.13776" class="word-anim word-96 group-1">
typing
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="1323.6736" y="790.18506" class="word-anim word-97 group-1">
strong
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="464.30768" y="402.37067" class="word-anim word-98 group-1">
unsafe
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="1100.5234" y="991.1818" class="word-anim word-99 group-1">
collector
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="976.8737" y="68.95435" class="word-anim word-100 group-1">
helpful
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="688.3566" y="367.00824" class="word-anim word-101 group-1">
data
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="1387.5874" y="645.4724" class="word-anim word-102 group-1">
crates
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="652.4785" y="984.27893" class="word-anim word-103 group-1">
result
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="27.555555" font-weight="bold" x="1278.4786" y="227.9987" class="word-anim word-104 group-1">
static
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1386.1342" y="471.3738" class="word-anim word-0 group-0">
semantic
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1389.3633" y="676.06714" class="word-anim word-1 group-0">
backwards
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="620.6754" y="636.56354" class="word-anim word-2 group-0">
const
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="892.3254" y="1046.9965" class="word-anim word-3 group-0">
versioning
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="680.8538" y="636.8752" class="word-anim word-4 group-0">
sync
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="511.32056" y="867.0855" class="word-anim word-5 group-0">
procedural
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="457.77112" y="649.01874" class="word-anim word-6 group-0">
process
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="457.92633" y="797.2702" class="word-anim word-7 group-0">
declarative
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1239.2181" y="172.84532" class="word-anim word-8 group-0">
embedded
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="745.6762" y="466.94366" class="word-anim word-9 group-0">
lsp
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1397.2759" y="397.6452" class="word-anim word-10 group-0">
raw
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="492.89383" y="296.7858" class="word-anim word-11 group-0">
edition
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1008.80786" y="1050.7838" class="word-anim word-12 group-0">
minimal
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="855.34564" y="45.520424" class="word-anim word-13 group-0">
formatter
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="508.17072" y="353.2789" class="word-anim word-14 group-0">
rust
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1391.0719" y="362.54025" class="word-anim word-15 group-0">
c-level
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1262.0265" y="923.743" class="word-anim word-16 group-0">
module
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="776.79205" y="1045.2869" class="word-anim word-17 group-0">
messages
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1184.4911" y="119.56299" class="word-anim word-18 group-0">
documentation
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="486.53806" y="267.74036" class="word-anim word-19 group-0">
explicit
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="465.06213" y="325.6163" class="word-anim word-20 group-0">
atomic
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1213.869" y="642.48706" class="word-anim word-21 group-0">
adt
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1101.3822" y="1029.9446" class="word-anim word-22 group-0">
functions
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="490.42038" y="828.8349" class="word-anim word-23 group-0">
privacy
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="535.33844" y="223.15245" class="word-anim word-24 group-0">
source
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1134.1338" y="64.58917" class="word-anim word-25 group-0">
interoperability
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="774.4403" y="359.69046" class="word-anim word-26 group-0">
c
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="649.3347" y="1017.0682" class="word-anim word-27 group-0">
webassembly
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="541.86816" y="179.02205" class="word-anim word-28 group-0">
functional
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1398.1829" y="322.73288" class="word-anim word-29 group-0">
overhead
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1382.4762" y="281.90527" class="word-anim word-30 group-0">
smart
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="582.999" y="91.88788" class="word-anim word-31 group-0">
compatibility
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1425.5944" y="729.7865" class="word-anim word-32 group-0">
build
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="563.09" y="142.66473" class="word-anim word-33 group-0">
ecosystem
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1087.8024" y="1057.8292" class="word-anim word-34 group-0">
algebraic
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1267.3213" y="956.00287" class="word-anim word-35 group-0">
objects
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="429.7509" y="445.2018" class="word-anim word-36 group-0">
rfc
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="448.99484" y="740.26135" class="word-anim word-37 group-0">
drop
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1197.3912" y="93.55542" class="word-anim word-38 group-0">
destruction
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="424.4677" y="355.15646" class="word-anim word-39 group-0">
native
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1136.1305" y="33.85862" class="word-anim word-40 group-0">
visibility
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="581.2579" y="957.7149" class="word-anim word-41 group-0">
clippy
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="713.579" y="35.523903" class="word-anim word-42 group-0">
benchmarking
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="611.08765" y="1051.0193" class="word-anim word-43 group-0">
higher-order
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1410.0359" y="818.8714" class="word-anim word-44 group-0">
open
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1332.556" y="178.83907" class="word-anim word-45 group-0">
package
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="533.04956" y="983.7404" class="word-anim word-46 group-0">
programming
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="520.29535" y="897.4652" class="word-anim word-47 group-0">
linter
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1205.2208" y="1031.385" class="word-anim word-48 group-0">
cross-compilation
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1480.5127" y="600.0406" class="word-anim word-49 group-0">
races
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1468.628" y="397.50375" class="word-anim word-50 group-0">
systems
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="396.79773" y="400.41342" class="word-anim word-51 group-0">
blocks
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1234.3291" y="146.41238" class="word-anim word-52 group-0">
stagnation
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="487.77454" y="229.03014" class="word-anim word-53 group-0">
cell
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="372.71103" y="649.337" class="word-anim word-54 group-0">
refcell
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1400.8318" y="254.39046" class="word-anim word-55 group-0">
generator
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1345.7454" y="929.5723" class="word-anim word-56 group-0">
llvm
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1275.8625" y="983.52106" class="word-anim word-57 group-0">
compilation
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="444.48532" y="766.3947" class="word-anim word-58 group-0">
send
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1438.4867" y="785.709" class="word-anim word-59 group-0">
analyzer
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="342.0386" y="515.68225" class="word-anim word-60 group-0">
built-in
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="443.12814" y="936.4471" class="word-anim word-61 group-0">
immutability
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1463.5414" y="703.7438" class="word-anim word-62 group-0">
testing
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="633.1204" y="58.991703" class="word-anim word-63 group-0">
runtime
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1476.968" y="470.50415" class="word-anim word-64 group-0">
manager
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1469.3732" y="360.08743" class="word-anim word-65 group-0">
control
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="368.93518" y="684.98846" class="word-anim word-66 group-0">
driven
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1397.6543" y="859.4224" class="word-anim word-67 group-0">
interior
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1500.7189" y="633.47577" class="word-anim word-68 group-0">
and
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1399.5933" y="222.50127" class="word-anim word-69 group-0">
backend
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="321.86945" y="485.26376" class="word-anim word-70 group-0">
associated
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="382.75775" y="581.5915" class="word-anim word-71 group-0">
wasm
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1441.367" y="756.9299" class="word-anim word-72 group-0">
suggestions
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1191.8885" y="1067.525" class="word-anim word-73 group-0">
rustfmt
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="324.25934" y="548.7356" class="word-anim word-74 group-0">
stability
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="365.49942" y="855.8461" class="word-anim word-75 group-0">
deterministic
</text>
</svg>

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -0,0 +1,634 @@
<svg height="1080" viewBox="0 0 1920 1080" width="1920" xmlns="http://www.w3.org/2000/svg">
<style>
@keyframes wordAppear-group-0 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.1% {
opacity: 1;
transform: scale(1);
}
20.7% {
opacity: 1;
transform: scale(1);
}
31.2% {
opacity: 1;
transform: scale(1);
}
34.0% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-1 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.1% {
opacity: 1;
transform: scale(1);
}
20.7% {
opacity: 1;
transform: scale(1);
}
41.1% {
opacity: 1;
transform: scale(1);
}
43.9% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-2 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.1% {
opacity: 1;
transform: scale(1);
}
20.7% {
opacity: 1;
transform: scale(1);
}
50.9% {
opacity: 1;
transform: scale(1);
}
53.7% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-3 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.1% {
opacity: 1;
transform: scale(1);
}
20.7% {
opacity: 1;
transform: scale(1);
}
60.7% {
opacity: 1;
transform: scale(1);
}
63.5% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-4 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.1% {
opacity: 1;
transform: scale(1);
}
20.7% {
opacity: 1;
transform: scale(1);
}
70.5% {
opacity: 1;
transform: scale(1);
}
73.3% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-5 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.1% {
opacity: 1;
transform: scale(1);
}
20.7% {
opacity: 1;
transform: scale(1);
}
80.4% {
opacity: 1;
transform: scale(1);
}
83.2% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes wordAppear-group-6 {
0% {
opacity: 0;
transform: scale(0.5);
}
2.1% {
opacity: 1;
transform: scale(1);
}
20.7% {
opacity: 1;
transform: scale(1);
}
90.2% {
opacity: 1;
transform: scale(1);
}
93.0% {
opacity: 0;
transform: scale(0.5);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
.word-anim {
animation-duration: 28.5s;
animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
animation-iteration-count: infinite;
animation-fill-mode: both;
transform-origin: center;
}
.word-0 { animation-delay: 0.00s; }
.word-1 { animation-delay: 0.05s; }
.word-2 { animation-delay: 0.10s; }
.word-3 { animation-delay: 0.15s; }
.word-4 { animation-delay: 0.20s; }
.word-5 { animation-delay: 0.25s; }
.word-6 { animation-delay: 0.30s; }
.word-7 { animation-delay: 0.35s; }
.word-8 { animation-delay: 0.40s; }
.word-9 { animation-delay: 0.45s; }
.word-10 { animation-delay: 0.50s; }
.word-11 { animation-delay: 0.55s; }
.word-12 { animation-delay: 0.60s; }
.word-13 { animation-delay: 0.65s; }
.word-14 { animation-delay: 0.70s; }
.word-15 { animation-delay: 0.75s; }
.word-16 { animation-delay: 0.80s; }
.word-17 { animation-delay: 0.85s; }
.word-18 { animation-delay: 0.90s; }
.word-19 { animation-delay: 0.95s; }
.word-20 { animation-delay: 1.00s; }
.word-21 { animation-delay: 1.05s; }
.word-22 { animation-delay: 1.10s; }
.word-23 { animation-delay: 1.15s; }
.word-24 { animation-delay: 1.20s; }
.word-25 { animation-delay: 1.25s; }
.word-26 { animation-delay: 1.30s; }
.word-27 { animation-delay: 1.35s; }
.word-28 { animation-delay: 1.40s; }
.word-29 { animation-delay: 1.45s; }
.word-30 { animation-delay: 1.50s; }
.word-31 { animation-delay: 1.55s; }
.word-32 { animation-delay: 1.60s; }
.word-33 { animation-delay: 1.65s; }
.word-34 { animation-delay: 1.70s; }
.word-35 { animation-delay: 1.75s; }
.word-36 { animation-delay: 1.80s; }
.word-37 { animation-delay: 1.85s; }
.word-38 { animation-delay: 1.90s; }
.word-39 { animation-delay: 1.95s; }
.word-40 { animation-delay: 2.00s; }
.word-41 { animation-delay: 2.05s; }
.word-42 { animation-delay: 2.10s; }
.word-43 { animation-delay: 2.15s; }
.word-44 { animation-delay: 2.20s; }
.word-45 { animation-delay: 2.25s; }
.word-46 { animation-delay: 2.30s; }
.word-47 { animation-delay: 2.35s; }
.word-48 { animation-delay: 2.40s; }
.word-49 { animation-delay: 2.45s; }
.word-50 { animation-delay: 2.50s; }
.word-51 { animation-delay: 2.55s; }
.word-52 { animation-delay: 2.60s; }
.word-53 { animation-delay: 2.65s; }
.word-54 { animation-delay: 2.70s; }
.word-55 { animation-delay: 2.75s; }
.word-56 { animation-delay: 2.80s; }
.word-57 { animation-delay: 2.85s; }
.word-58 { animation-delay: 2.90s; }
.word-59 { animation-delay: 2.95s; }
.word-60 { animation-delay: 3.00s; }
.word-61 { animation-delay: 3.05s; }
.word-62 { animation-delay: 3.10s; }
.word-63 { animation-delay: 3.15s; }
.word-64 { animation-delay: 3.20s; }
.word-65 { animation-delay: 3.25s; }
.word-66 { animation-delay: 3.30s; }
.word-67 { animation-delay: 3.35s; }
.word-68 { animation-delay: 3.40s; }
.word-69 { animation-delay: 3.45s; }
.word-70 { animation-delay: 3.50s; }
.word-71 { animation-delay: 3.55s; }
.word-72 { animation-delay: 3.60s; }
.word-73 { animation-delay: 3.65s; }
.word-74 { animation-delay: 3.70s; }
.word-75 { animation-delay: 3.75s; }
.word-76 { animation-delay: 3.80s; }
.word-77 { animation-delay: 3.85s; }
.word-78 { animation-delay: 3.90s; }
.word-79 { animation-delay: 3.95s; }
.word-80 { animation-delay: 4.00s; }
.word-81 { animation-delay: 4.05s; }
.word-82 { animation-delay: 4.10s; }
.word-83 { animation-delay: 4.15s; }
.word-84 { animation-delay: 4.20s; }
.word-85 { animation-delay: 4.25s; }
.word-86 { animation-delay: 4.30s; }
.word-87 { animation-delay: 4.35s; }
.word-88 { animation-delay: 4.40s; }
.word-89 { animation-delay: 4.45s; }
.word-90 { animation-delay: 4.50s; }
.word-91 { animation-delay: 4.55s; }
.word-92 { animation-delay: 4.60s; }
.word-93 { animation-delay: 4.65s; }
.word-94 { animation-delay: 4.70s; }
.word-95 { animation-delay: 4.75s; }
.word-96 { animation-delay: 4.80s; }
.word-97 { animation-delay: 4.85s; }
.word-98 { animation-delay: 4.90s; }
.word-99 { animation-delay: 4.95s; }
.word-100 { animation-delay: 5.00s; }
.word-101 { animation-delay: 5.05s; }
.word-102 { animation-delay: 5.10s; }
.word-103 { animation-delay: 5.15s; }
.word-104 { animation-delay: 5.20s; }
.word-105 { animation-delay: 5.25s; }
.group-0 { animation-name: wordAppear-group-0; }
.group-1 { animation-name: wordAppear-group-1; }
.group-2 { animation-name: wordAppear-group-2; }
.group-3 { animation-name: wordAppear-group-3; }
.group-4 { animation-name: wordAppear-group-4; }
.group-5 { animation-name: wordAppear-group-5; }
.group-6 { animation-name: wordAppear-group-6; }
</style>
<rect fill="#1a1a1a" height="100%" width="100%"/>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="120" font-weight="bold" x="541.2312" y="635.185" class="word-anim word-105 group-6">
programming
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="68" font-weight="bold" x="833.0145" y="473.13858" class="word-anim word-104 group-5">
design
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="60.57143" font-weight="bold" x="753.55176" y="722.60284" class="word-anim word-102 group-5">
development
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="60.57143" font-weight="bold" x="684.23816" y="379.37448" class="word-anim word-103 group-5">
architecture
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="45.714287" font-weight="bold" x="878.34" y="791.4736" class="word-anim word-98 group-4">
cloud
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="45.714287" font-weight="bold" x="702.1706" y="471.26508" class="word-anim word-99 group-4">
rust
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="45.714287" font-weight="bold" x="879.0907" y="292.89056" class="word-anim word-100 group-4">
testing
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="45.714287" font-weight="bold" x="1105.8947" y="473.24802" class="word-anim word-101 group-4">
code
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="38.285713" font-weight="bold" x="800.8689" y="847.3973" class="word-anim word-96 group-3">
administration
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="38.285713" font-weight="bold" x="707.4884" y="786.6909" class="word-anim word-97 group-3">
devops
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="30.857143" font-weight="bold" x="1039.0612" y="780.6628" class="word-anim word-82 group-2">
problem
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="30.857143" font-weight="bold" x="1174.844" y="403.83902" class="word-anim word-83 group-2">
solving
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="30.857143" font-weight="bold" x="725.4653" y="294.94424" class="word-anim word-84 group-2">
systems
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="30.857143" font-weight="bold" x="600.1453" y="687.464" class="word-anim word-85 group-2">
docker
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="30.857143" font-weight="bold" x="917.83276" y="227.14305" class="word-anim word-86 group-2">
memory
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="30.857143" font-weight="bold" x="844.23694" y="895.9254" class="word-anim word-87 group-2">
microservices
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="30.857143" font-weight="bold" x="1195.2705" y="725.66504" class="word-anim word-88 group-2">
technical
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="30.857143" font-weight="bold" x="1094.3486" y="288.34555" class="word-anim word-89 group-2">
async
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="30.857143" font-weight="bold" x="1230.0496" y="466.43332" class="word-anim word-90 group-2">
ci/cd
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="30.857143" font-weight="bold" x="750.0815" y="248.87617" class="word-anim word-91 group-2">
safety
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="30.857143" font-weight="bold" x="1167.3761" y="347.82788" class="word-anim word-92 group-2">
api
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="30.857143" font-weight="bold" x="550.1161" y="441.25406" class="word-anim word-93 group-2">
backend
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="30.857143" font-weight="bold" x="859.0265" y="180.01949" class="word-anim word-94 group-2">
performance
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="30.857143" font-weight="bold" x="1186.1056" y="771.2332" class="word-anim word-95 group-2">
aws
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="1222.5597" y="674.30786" class="word-anim word-50 group-1">
patterns
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="594.52966" y="731.44635" class="word-anim word-51 group-1">
container
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="641.11633" y="826.73004" class="word-anim word-52 group-1">
principles
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="887.6066" y="931.81256" class="word-anim word-53 group-1">
orchestration
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="1066.223" y="227.29723" class="word-anim word-54 group-1">
coding
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="703.5277" y="886.2658" class="word-anim word-55 group-1">
database
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="581.68365" y="370.45404" class="word-anim word-56 group-1">
system
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="1156.2684" y="817.2045" class="word-anim word-57 group-1">
optimization
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="1114.3265" y="888.8629" class="word-anim word-58 group-1">
observability
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="541.7903" y="777.53864" class="word-anim word-59 group-1">
automation
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="1248.8236" y="348.2394" class="word-anim word-60 group-1">
git
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="924.475" y="967.7591" class="word-anim word-61 group-1">
kubernetes
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="1210.9502" y="300.29785" class="word-anim word-62 group-1">
rest
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="761.58185" y="191.78325" class="word-anim word-63 group-1">
native
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="652.5001" y="286.3378" class="word-anim word-64 group-1">
apis
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="754.4686" y="932.01166" class="word-anim word-65 group-1">
security
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="838.3574" y="133.60187" class="word-anim word-66 group-1">
monitoring
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="1173.5773" y="853.0092" class="word-anim word-67 group-1">
review
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="1092.8752" y="188.75966" class="word-anim word-68 group-1">
leadership
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="561.6484" y="334.2422" class="word-anim word-69 group-1">
graphql
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="697.2429" y="969.9786" class="word-anim word-70 group-1">
infrastructure
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="1251.5156" y="774.85736" class="word-anim word-71 group-1">
webassembly
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="603.08093" y="234.34297" class="word-anim word-72 group-1">
mentoring
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="1171.9418" y="242.05544" class="word-anim word-73 group-1">
grpc
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="1119.0935" y="925.2806" class="word-anim word-74 group-1">
linux
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="845.7132" y="1009.27875" class="word-anim word-75 group-1">
authentication
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="531.4282" y="676.2987" class="word-anim word-76 group-1">
vibe
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="619.4345" y="197.84349" class="word-anim word-77 group-1">
terraform
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="1031.6455" y="125.624176" class="word-anim word-78 group-1">
concurrent
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="1331.5536" y="421.1789" class="word-anim word-79 group-1">
as
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="679.30164" y="151.13089" class="word-anim word-80 group-1">
postgresql
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="23.428572" font-weight="bold" x="1344.4178" y="463.03943" class="word-anim word-81 group-1">
agile
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="622.97833" y="471.62384" class="word-anim word-0 group-0">
github
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="744.5366" y="405.32407" class="word-anim word-1 group-0">
network
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1281.4167" y="299.3928" class="word-anim word-2 group-0">
azure
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="550.1165" y="467.6307" class="word-anim word-3 group-0">
lambda
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1093.9832" y="959.32874" class="word-anim word-4 group-0">
functions
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="606.8129" y="856.4638" class="word-anim word-5 group-0">
frontend
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1247.2119" y="260.65515" class="word-anim word-6 group-0">
serverless
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1309.6407" y="346.25778" class="word-anim word-7 group-0">
debugging
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="680.92334" y="920.831" class="word-anim word-8 group-0">
queues
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="869.14856" y="94.13608" class="word-anim word-9 group-0">
authorization
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="574.23975" y="297.32736" class="word-anim word-10 group-0">
grafana
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1094.2676" y="153.14973" class="word-anim word-11 group-0">
websockets
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1106.1826" y="408.2148" class="word-anim word-12 group-0">
unit
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1321.3738" y="373.88638" class="word-anim word-13 group-0">
integration
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="579.71454" y="886.22534" class="word-anim word-14 group-0">
reliability
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="442.07797" y="571.1288" class="word-anim word-15 group-0">
scripting
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="539.4134" y="807.41144" class="word-anim word-16 group-0">
writing
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1350.8396" y="666.7895" class="word-anim word-17 group-0">
shell
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="498.43784" y="706.39606" class="word-anim word-18 group-0">
logging
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1243.4211" y="234.02495" class="word-anim word-19 group-0">
solid
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1012.4911" y="84.70685" class="word-anim word-20 group-0">
documentation
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="542.7672" y="389.1769" class="word-anim word-21 group-0">
gcp
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="474.31268" y="736.11383" class="word-anim word-22 group-0">
refactoring
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="876.0713" y="220.10298" class="word-anim word-23 group-0">
sre
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="707.8421" y="107.408264" class="word-anim word-24 group-0">
event-driven
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="429.58777" y="457.10724" class="word-anim word-25 group-0">
engineering
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="527.86957" y="267.3322" class="word-anim word-26 group-0">
ai-enhanced
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1277.5687" y="849.05505" class="word-anim word-27 group-0">
prometheus
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="485.97092" y="362.27112" class="word-anim word-28 group-0">
message
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="473.16574" y="620.7535" class="word-anim word-29 group-0">
oauth2
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="477.92" y="500.09912" class="word-anim word-30 group-0">
stack
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="496.29108" y="537.2645" class="word-anim word-31 group-0">
ssl
</text>
<text fill="#ffffff" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1210.7102" y="915.4218" class="word-anim word-32 group-0">
scrum
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1078.7523" y="998.69684" class="word-anim word-33 group-0">
benchmarking
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1404.981" y="550.3839" class="word-anim word-34 group-0">
redis
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1383.5399" y="423.69046" class="word-anim word-35 group-0">
tls
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="631.9537" y="916.761" class="word-anim word-36 group-0">
http
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="944.01843" y="1037.317" class="word-anim word-37 group-0">
profiling
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1346.9395" y="315.23062" class="word-anim word-38 group-0">
gitlab
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="500.86472" y="426.79297" class="word-anim word-39 group-0">
dry
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="782.765" y="997.9095" class="word-anim word-40 group-0">
nosql
</text>
<text fill="#62c0cc" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1203.1738" y="941.65295" class="word-anim word-41 group-0">
full
</text>
<text fill="#a72145" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="566.12634" y="839.59064" class="word-anim word-42 group-0">
jwt
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1406.3193" y="592.1862" class="word-anim word-43 group-0">
https
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="893.83344" y="54.43308" class="word-anim word-44 group-0">
test-driven
</text>
<text fill="#f74c00" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="471.12463" y="388.98685" class="word-anim word-45 group-0">
tcp/ip
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1252.1326" y="199.47649" class="word-anim word-46 group-0">
site
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1385.5791" y="705.4199" class="word-anim word-47 group-0">
mongodb
</text>
<text fill="#ce422b" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="1414.529" y="522.71277" class="word-anim word-48 group-0">
clean
</text>
<text fill="#ffa724" font-family="Victor Mono, monospace" font-size="16" font-weight="bold" x="482.55618" y="660.7475" class="word-anim word-49 group-0">
pair
</text>
</svg>

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,898 @@
# Rustikon 2026 - Live Reader (30-Min Timing)
Total target: 30:00 (1800s)
How to use:
- Keep your pace near each slide target.
- If you are late, cut one example line and move on.
- If you are early, add one short emphasis line.
## Slide 1 [target: 20s | cumulative: 00:20]
STORY: Act 1 - Hook & Promise
TENSION: Low -> Medium
EMPHASIS: Credibilidad y dolor real
DELIVERY: Calma, pausas limpias
Why I Needed Rust
Finally. / infra Automation I Can Sleep On
## Slide 2 [target: 54s | cumulative: 01:14]
STORY: Act 1 - Hook & Promise
TENSION: Low -> Medium
EMPHASIS: Credibilidad y dolor real
DELIVERY: Calma, pausas limpias
Years. / One Problem.
Era Tool Lesson
1990s Perl Power without safety is a disaster
2000s Python practical work without safety rules is fragile
2010s Bash · Chef · Ansible · Terraform More tools don't solve approach problems
2020s Go · ???
Each time. / I thought I had the answer.
Each time. / reality proved me wrong.
## Slide 3 [target: 17s | cumulative: 01:31]
STORY: Act 1 - Hook & Promise
TENSION: Low -> Medium
EMPHASIS: Credibilidad y dolor real
DELIVERY: Calma, pausas limpias
The Evolution
How we got here
## Slide 4 [target: 77s | cumulative: 02:48]
STORY: Act 2 - Escalation
TENSION: Medium -> High
EMPHASIS: Crecimiento de complejidad sin control
DELIVERY: Ritmo creciente
Stage 1. / Local (late 80s / early 90s)
Dumb terminals. / Single machine. / One state.
Local development. / long deployment cycles. / low urgency
One state. / easy to observe. / easy to control
I A C: procedural scripts. / logic hidden inside the application
> The Perl Era: we could do anything.
> We could also break anything.
> Beautiful. / terrifying metaprogramming.
> No safety net.
> Silent failures at 3 AM.
> Lesson: power without safety is a disaster.
🛡 ●●●●○ &nbsp; 😴 ●●●●○ &nbsp; 🔥 ●○○○○
## Slide 5 [target: 87s | cumulative: 04:15]
STORY: Act 2 - Escalation
TENSION: Medium -> High
EMPHASIS: Crecimiento de complejidad sin control
DELIVERY: Ritmo creciente
Stage 2. / Networks / Internet
Systems getting farther away. / More people. / More coordination.
Remote access. / spread out teams. / security becomes relevant
Cost of downtime rises. / processes become critical
Harmonizing: package installs. / config. / updates across multiple machines in parallel
I A C: reproducible automation. / first declared attempts
> The Python Era: rapid development. / great community.
> But nothing stopped you from being wrong.
> Type hints came late. / and optional.
> Runtime errors >> compile-time errors.
> Lesson: practical work without safety rules is fragile.
More pieces. / More people. / Getting interesting.
🛡 ●●●○○ &nbsp; 😴 ●●●○○ &nbsp; 🔥 ●●○○○
## Slide 6 [target: 85s | cumulative: 05:40]
STORY: Act 2 - Escalation
TENSION: Medium -> High
EMPHASIS: Crecimiento de complejidad sin control
DELIVERY: Ritmo creciente
Stage 3. / Containers / Cloud / CI-CD
Everything. / Everywhere. / All at once.
Monolith → spread out. / all day. / every day. / high availability
Cloud. / hybrid. / multi-cloud. / on-prem. / simultaneously
Rollback and rollforward: database transactions. / but for infra
Scale horizontally AND vertically. / and descale
C I slash C D continuous: new features. / new deploys. / permanent churn
> The Cloud/I A C Era: Ansible. / Terraform. / Chef. / Puppet.
> What changed? The syntax.
> What didn't? The fundamental problems.
> Still fighting type safety. / Still discovering errors in production.
> Lesson: more tools don't solve approach problems.
🛡 ●●○○○ &nbsp; 😴 ●○○○○ &nbsp; 🔥 ●●●●○
## Slide 7 [target: 27s | cumulative: 06:07]
STORY: Act 2 - Escalation
TENSION: Medium -> High
EMPHASIS: Crecimiento de complejidad sin control
DELIVERY: Ritmo creciente
I could automate infra.
But I couldn't make it reliable.
I couldn't prevent mistakes.
I couldn't sleep.
🛡 ●○○○○ &nbsp;&nbsp; 😴 ○○○○○ &nbsp;&nbsp; 🔥 ●●●●●
## Slide 8 [target: 17s | cumulative: 06:24]
STORY: Act 3 - Problem Anatomy
TENSION: High
EMPHASIS: Causa raiz y costo de fallar tarde
DELIVERY: Didactico, frases cortas
Why I A C Fails
The restaurant problem
## Slide 9 [target: 90s | cumulative: 07:54]
STORY: Act 3 - Problem Anatomy
TENSION: High
EMPHASIS: Causa raiz y costo de fallar tarde
DELIVERY: Didactico, frases cortas
The Restaurant
Every restaurant has at least three actors.
Restaurant infra
Guest declares what they want declared config (YAML. / HCL)
Waiter validates and transmits controller (K8s. / Ansible)
Kitchen executes and delivers Runtime / setup
Dish arrives. / or doesn't Deployment succeeds. / or not
What makes it work. / or not:
The guest declares. / Doesn't implement.
The waiter must know what's possible. / before going to the kitchen.
> "I want X" → waiter goes to kitchen
> → "we don't have X. / why is it on the menu?"
> → back to the table.
> Equivalent: I configured a host with port 8443 → that port isn't allowed → reconfigure from zero.
## Slide 10 [target: 90s | cumulative: 09:24]
STORY: Act 3 - Problem Anatomy
TENSION: High
EMPHASIS: Causa raiz y costo de fallar tarde
DELIVERY: Didactico, frases cortas
The Truth That Mutates
State is not static. / It can change at every step of the chain.
Step Truth for this actor
Guest speaks What they want
Waiter's notepad What was written down
Kitchen markings What's done / not done
Payment ticket What was actually served
> Fail early = fail cheap. / Fail in production = nightmare.
The context problem:
The waiter knows the regular customer: "always no salt."
The kitchen doesn't. / If the waiter changes. / that context disappears.
config drift is the same thing: Implicit state. / Not explicit. / Not propagated. / Lost silently.
The cost of failure depends on where it happens:
Fail at the table (impossible order): cheap. / caught before kitchen
Fail in kitchen (ingredient missing): medium. / renegotiate with guest
Fail at delivery (wrong dish arrives): expensive. / experience destroyed
## Slide 11 [target: 61s | cumulative: 10:25]
STORY: Act 3 - Problem Anatomy
TENSION: High
EMPHASIS: Causa raiz y costo de fallar tarde
DELIVERY: Didactico, frases cortas
"We Don't Have Mushrooms"
When an actor in the chain can't fulfill part of the order.
> "Can I substitute vegetables?"
> That renegotiation must be explicit. / Traced. / Re-authorized. / Not silent. / Not assumed.
config drift is silent renegotiation:
The system changes. / Nobody notified. / State diverges without trace.
Rust's answer. / Option :
[code example]
> The compiler check is the waiter who cannot pretend an ingredient exists.
## Slide 12 [target: 90s | cumulative: 11:55]
STORY: Act 3 - Problem Anatomy
TENSION: High
EMPHASIS: Causa raiz y costo de fallar tarde
DELIVERY: Didactico, frases cortas
The Config Evolution
How we got from code to YAML hell
Hardcoded. / everything inside the binary. / Full control. / Zero flexibility.
External config (JSON). / works between machines. / Unreadable for humans at scale.
YAML / TOML. / more readable. / Fragile syntax. / Implicit types. / Silent errors.
YAML + Serde. / Serde validates the structure:
Does the field exist? Is it the right type?
Do we accept "elephant" as a pet? If the type is String... / yes.
Serde validates shape. / Not meaning.
Helm / Jinja templates. / YAML generated from variables (in YAML).
Does it validate the content of the generated YAML? No. / Not at all.
Like using an LLM with a markdown reference: the format is there.
but is the content correct? Nobody safety rules that.
## Slide 13 [target: 23s | cumulative: 12:18]
STORY: Act 3 - Problem Anatomy
TENSION: High
EMPHASIS: Causa raiz y costo de fallar tarde
DELIVERY: Didactico, frases cortas
Continuous C I slash C D.
No semantic check.
Continuous hope.
(crossing our fingers in production)
🛡 ●○○○○ &nbsp;&nbsp; 😴 ○○○○○ &nbsp;&nbsp; 🔥 ●●●●●
## Slide 14 [target: 90s | cumulative: 13:48]
STORY: Act 3 - Problem Anatomy
TENSION: High
EMPHASIS: Causa raiz y costo de fallar tarde
DELIVERY: Didactico, frases cortas
Three Questions Without Answers
Question 1 —
— Why do we wait for things to break?
"Works on my machine". / in production. / I don't know
Fail late = maximum cost. / We want: fail fast. / fail cheap
Question 2 —
— Do we actually know what we want?
Is the declaration sufficient and consistent with what's possible?
What are the boundaries? Static or dynamic? What is the source of truth. / and when does it mutate?
Question 3 —
Can we guarantee determinism ?
C I slash C D without semantic check = continuous hope
We want certainty. / not randomness
"Works on my machine" cannot be the production standard
> We're not inventing anything new. / Everything already exists.
> The question is whether we're managing it correctly.
## Slide 15 [target: 19s | cumulative: 14:07]
STORY: Act 4 - Turning Point
TENSION: Peak
EMPHASIS: No faltan tools, falta paradigma
DELIVERY: Silencios intencionales
The tools weren't the problem.
The languages weren't the problem.
The approach was the problem.
## Slide 16 [target: 35s | cumulative: 14:42]
STORY: Act 4 - Turning Point
TENSION: Peak
EMPHASIS: No faltan tools, falta paradigma
DELIVERY: Silencios intencionales
Systems we don't know how to control.
We hope they work.
When they don't. / we fix them.
Continuous nightmare.
( alarm state as the new normal)
🛡 ●○○○○ &nbsp;&nbsp; 😴 ○○○○○ &nbsp;&nbsp; 🔥 ●●●●●
## Slide 17 [target: 17s | cumulative: 14:59]
STORY: Act 4 - Turning Point
TENSION: Peak
EMPHASIS: No faltan tools, falta paradigma
DELIVERY: Silencios intencionales
Rust
The answer to all three questions
## Slide 18 [target: 51s | cumulative: 15:50]
STORY: Act 4 - Turning Point
TENSION: Peak
EMPHASIS: No faltan tools, falta paradigma
DELIVERY: Silencios intencionales
The Bridge: From Serde to Types
Serde loads structurally valid config.
But "elephant" as pet: String compiles.
Rust's answer: don't use String. / Use a type.
[code example]
This is the shift.:
Not the config format. / The model of what it can contain.
Serde validates shape Types validate meaning
The compiler check validates before the binary exists.
## Slide 19 [target: 24s | cumulative: 16:14]
STORY: Act 5 - Resolution
TENSION: High -> Medium
EMPHASIS: Types y compiler como respuesta
DELIVERY: Claro y tecnico
What Rust Gives Us
Answer to Question 1: fail early. / fail cheap
[code example]
Answer to Question 2: explicit contracts
[code example]
## Slide 20 [target: 64s | cumulative: 17:18]
STORY: Act 5 - Resolution
TENSION: High -> Medium
EMPHASIS: Types y compiler como respuesta
DELIVERY: Claro y tecnico
The compiler check as Pre-Validator
Answer to Question 3:
guaranteed determinism
[code example]
The compiler check validates:
Before building the binary
Not after hours of execution
Not when a function nobody touched in months finally gets called
Predictable behavior: memory. / resources. / workflows
The compiler check is the waiter who validates the order.
Before it reaches the kitchen.
Before the guest waits.
Before any ingredient is missing.
🛡 ●●●●○ &nbsp; 😴 ●●●●○ &nbsp; 🔥 ●●○○○
## Slide 21 [target: 53s | cumulative: 18:11]
STORY: Act 5 - Resolution
TENSION: High -> Medium
EMPHASIS: Types y compiler como respuesta
DELIVERY: Claro y tecnico
The Human Impact
When the system is trustworthy:
✓ Sleep comes back
✓ Confidence returns
✓ The team trusts the automation
✓ Stress decreases
✓ You can actually rest
> What you can't measure: fear .
> What you can measure: MTTR .
> Before: > 30 minutes. / Now: .
🛡 ●●●●●&nbsp; 😴 ●●●●●&nbsp; 🔥 ●○○○○
## Slide 22 [target: 22s | cumulative: 18:33]
STORY: Act 5 - Resolution
TENSION: High -> Medium
EMPHASIS: Types y compiler como respuesta
DELIVERY: Claro y tecnico
Continuous C I slash C D.
Types. / compiler check. / Explicit state.
Continuous certainty.
(to keep sleeping well)
🛡 ●●●●● &nbsp;&nbsp; 😴 ●●●●● &nbsp;&nbsp; 🔥 ●○○○○
## Slide 23 [target: 16s | cumulative: 18:49]
STORY: Act 5 - Resolution
TENSION: High -> Medium
EMPHASIS: Types y compiler como respuesta
DELIVERY: Claro y tecnico
In Production
This is not theory
## Slide 24 (name: provisioning) [target: 36s | cumulative: 19:25]
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
import { ref. / computed } from 'vue'
const useWhite = ref(false)
const bgStyle = computed(() => ({
backgroundImage: url('${useWhite.value ? "/w-arch-diag-v2.svg" : "/arch-diag-v2.svg"}').
backgroundSize: 'contain'.
backgroundRepeat: 'no-repeat'.
backgroundPosition: 'center'.
position: 'absolute'.
inset: '0'.
cursor: 'pointer'.
## Slide 25 [target: 84s | cumulative: 20:49]
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
Nickel
YAML rejected. / TOML rejected. / Reason: no type safety.
YAML wrote what we wanted.
It couldn't say what was possible.
Nickel closes that gap
— at config time. / not at 3 AM.
[code example]
Typed Source of Truth
Result (ADR-003): zero config type errors in production.
Config hierarchy:
defaults → workspace → profile → environment → runtime
Each layer merges.
Type system catches conflicts.
At config time. / not deployment time.
The guest wrote an impossible order.
Nickel makes impossible orders unwritable.
> Serde validates shape.
> Nickel validates meaning.
> The compiler check validates before deployment.
## Slide 26 [target: 44s | cumulative: 21:33]
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
Traits as Provider
The kitchen can change. / AWS ≠ UpCloud ≠ bare metal. / Same menu.
[code example]
Explicit state. / no drift:
[code example]
Contracts
Checkpoint every 5 minutes
No implicit state.
> No "the waiter remembers the customer doesn't want salt."
It's in the order. / Always. / Explicit.
## Slide 27 [target: 84s | cumulative: 22:57]
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
Dependency Graph
Failfast: bool is not a config option.
It's a principle encoded as a type.
Typed DAG. / dependency resolution enforced at workflow compile time:
The kitchen doesn't serve the main course before the starter is done.
DependencyType::Hard is that rule. / In the type system. / not in a runbook.
[code example]
Fail Fast. / Fail Cheap
DependencyType::Hard - failure stops the chain. / Always.
DependencyType::Soft - continues. / explicitly degraded.
DependencyType::Optional - missing is expected and fine.
> The compiler check catches the install order.
> Not the on-call engineer at 2 AM.
🛡 ●●●●● &nbsp; 😴 ●●●●● &nbsp; 🔥 ●○○○○
## Slide 28 [target: 90s | cumulative: 24:27]
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
Real Applications
## Kubernetes
The controller provisions cluster components as a typed workflow:
[code example]
Each dependency is a DependencyType.
The compiler check catches: installing Cilium without Kubernetes.
Not the on-call engineer at 2 AM.
>"In my machine it works" has a price here.
> This is the highest-stakes infra in the deck.
## Blockchain Validators
Validators require brutal uptime.
A validator that fails loses funds. / not your infra's money.
Your customer's.
Post-quantum cryptography: CRYSTALS-Kyber + Falcon + AES-256-GCM hybrid. / Validator keys protected against quantum computers.
SLOs with real error budgets: 99.99% = 52.6 min downtime/year. / Prometheus blocks deploys when burn rate exceeds budget.
Deterministic config: validator parameters are types. / A bondamount that isn't a valid u128 doesn't compile.
## Slide 29 [target: 82s | cumulative: 25:49]
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
Disaster Recovery
Rollback as a type. / not a procedure
3 AM. / Something broke. / You need to rollback.
Without types: you improvise.
With types: you choose a strategy
— or it doesn't compile.
[code example]
Multi-backend backup: restic. / borg. / tar. / rsync
— all as enum variants.
Production backup and DR restore use the same type. / the same schema.
> The runbook exists.
> Nobody reads it clearly at 3 AM under pressure.
> The type forces the decision before the crisis.
> The state is the same in prod and in DR. / Always.
## Slide 30 [target: 60s | cumulative: 26:49]
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
Self-Healing
> When something breaks at 3 AM
>. / the system responds. / not you.
[code example]
— Typed Remediation
What happens at 3 AM:
Alert fires → RemediationEngine matches condition → runs RestartService
Works: silent. / Nobody woken up.
Fails 3×: page sent. / with full state. / checkpoint. / and execution history.
> You wake up to information. / Not to chaos.
🛡 ●●●●● &nbsp; 😴 ●●●●● &nbsp; 🔥 ●○○○○
## Slide 31 [target: 32s | cumulative: 27:21]
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
Without types. / Without compiler check. / Without explicit state.
MTTR > 30 minutes.
Rust. / Types. / Explicit state.
Automated response.
MTTR (at 3 AM. / without you.)
🛡 ●●●●● &nbsp;&nbsp; 😴 ●●●●● &nbsp;&nbsp; 🔥 ●○○○○
## Slide 32 [target: 16s | cumulative: 27:37]
STORY: Act 7 - Close & CTA
TENSION: Medium -> Low
EMPHASIS: Cierre emocional con accion
DELIVERY: Lento y memorable
Why This Matters
For everyone in this room
## Slide 33 [target: 59s | cumulative: 28:36]
STORY: Act 7 - Close & CTA
TENSION: Medium -> Low
EMPHASIS: Cierre emocional con accion
DELIVERY: Lento y memorable
For You
If you've been frustrated like me
Rust solves problems you already have.
This is not hype. / It is operational relief.
Start here:
Model your infra as types
Let the compiler check pre-validate before deployment
If you're earlier in your career
Start with type safety from day one.
Build for stable work. / not only speed.
The shortest path:
Types for config.
Traits for providers.
Determinism for operations.
## Slide 34 [target: 40s | cumulative: 29:16]
STORY: Act 7 - Close & CTA
TENSION: Medium -> Low
EMPHASIS: Cierre emocional con accion
DELIVERY: Lento y memorable
I have perspective from long production experience.
I have seen technologies come and go.
Rust is not hype. / Rust is relief with evidence.
It solves real operational problems I had for decades.
More years is not a liability.
It is an advantage.
## Slide 35 (name: end) [target: 44s | cumulative: 30:00]
STORY: Act 7 - Close & CTA
TENSION: Medium -> Low
EMPHASIS: Cierre emocional con accion
DELIVERY: Lento y memorable
Why I Needed Rust
Three Closing Lines
I have lived this problem for decades.
Rust gave me deterministic systems and better sleep.
Start small: model infra as types.
Thank you. / Questions?
More info:
· jesusperez.pro
· setup.systems · vapora.dev · rustelo.dev
## Checkpoints
- 05:00 -> around Slide 6
- 10:00 -> around Slide 11
- 15:00 -> around Slide 18
- 20:00 -> around Slide 25
- 25:00 -> around Slide 29
- 30:00 -> around Slide 35

View File

@ -0,0 +1,885 @@
# Rustikon 2026 - Live Reader Script (Speech Only)
Only lines to speak. Legend: `/` short pause, `//` long pause.
## Slide 1
STORY: Act 1 - Hook & Promise
TENSION: Low -> Medium
EMPHASIS: Credibilidad y dolor real
DELIVERY: Calma, pausas limpias
Why I Needed Rust
Finally. / infra Automation I Can Sleep On
## Slide 2
STORY: Act 1 - Hook & Promise
TENSION: Low -> Medium
EMPHASIS: Credibilidad y dolor real
DELIVERY: Calma, pausas limpias
Years. / One Problem.
Era Tool Lesson
1990s Perl Power without safety is a disaster
2000s Python practical work without safety rules is fragile
2010s Bash · Chef · Ansible · Terraform More tools don't solve approach problems
2020s Go · ???
Each time. / I thought I had the answer.
Each time. / reality proved me wrong.
## Slide 3
STORY: Act 1 - Hook & Promise
TENSION: Low -> Medium
EMPHASIS: Credibilidad y dolor real
DELIVERY: Calma, pausas limpias
The Evolution
How we got here
## Slide 4
STORY: Act 2 - Escalation
TENSION: Medium -> High
EMPHASIS: Crecimiento de complejidad sin control
DELIVERY: Ritmo creciente
Stage 1. / Local (late 80s / early 90s)
Dumb terminals. / Single machine. / One state.
Local development. / long deployment cycles. / low urgency
One state. / easy to observe. / easy to control
I A C: procedural scripts. / logic hidden inside the application
> The Perl Era: we could do anything.
> We could also break anything.
> Beautiful. / terrifying metaprogramming.
> No safety net.
> Silent failures at 3 AM.
> Lesson: power without safety is a disaster.
🛡 ●●●●○ &nbsp; 😴 ●●●●○ &nbsp; 🔥 ●○○○○
## Slide 5
STORY: Act 2 - Escalation
TENSION: Medium -> High
EMPHASIS: Crecimiento de complejidad sin control
DELIVERY: Ritmo creciente
Stage 2. / Networks / Internet
Systems getting farther away. / More people. / More coordination.
Remote access. / spread out teams. / security becomes relevant
Cost of downtime rises. / processes become critical
Harmonizing: package installs. / config. / updates across multiple machines in parallel
I A C: reproducible automation. / first declared attempts
> The Python Era: rapid development. / great community.
> But nothing stopped you from being wrong.
> Type hints came late. / and optional.
> Runtime errors >> compile-time errors.
> Lesson: practical work without safety rules is fragile.
More pieces. / More people. / Getting interesting.
🛡 ●●●○○ &nbsp; 😴 ●●●○○ &nbsp; 🔥 ●●○○○
## Slide 6
STORY: Act 2 - Escalation
TENSION: Medium -> High
EMPHASIS: Crecimiento de complejidad sin control
DELIVERY: Ritmo creciente
Stage 3. / Containers / Cloud / CI-CD
Everything. / Everywhere. / All at once.
Monolith → spread out. / all day. / every day. / high availability
Cloud. / hybrid. / multi-cloud. / on-prem. / simultaneously
Rollback and rollforward: database transactions. / but for infra
Scale horizontally AND vertically. / and descale
C I slash C D continuous: new features. / new deploys. / permanent churn
> The Cloud/I A C Era: Ansible. / Terraform. / Chef. / Puppet.
> What changed? The syntax.
> What didn't? The fundamental problems.
> Still fighting type safety. / Still discovering errors in production.
> Lesson: more tools don't solve approach problems.
🛡 ●●○○○ &nbsp; 😴 ●○○○○ &nbsp; 🔥 ●●●●○
## Slide 7
STORY: Act 2 - Escalation
TENSION: Medium -> High
EMPHASIS: Crecimiento de complejidad sin control
DELIVERY: Ritmo creciente
I could automate infra.
But I couldn't make it reliable.
I couldn't prevent mistakes.
I couldn't sleep.
🛡 ●○○○○ &nbsp;&nbsp; 😴 ○○○○○ &nbsp;&nbsp; 🔥 ●●●●●
## Slide 8
STORY: Act 3 - Problem Anatomy
TENSION: High
EMPHASIS: Causa raiz y costo de fallar tarde
DELIVERY: Didactico, frases cortas
Why I A C Fails
The restaurant problem
## Slide 9
STORY: Act 3 - Problem Anatomy
TENSION: High
EMPHASIS: Causa raiz y costo de fallar tarde
DELIVERY: Didactico, frases cortas
The Restaurant
Every restaurant has at least three actors.
Restaurant infra
Guest declares what they want declared config (YAML. / HCL)
Waiter validates and transmits controller (K8s. / Ansible)
Kitchen executes and delivers Runtime / setup
Dish arrives. / or doesn't Deployment succeeds. / or not
What makes it work. / or not:
The guest declares. / Doesn't implement.
The waiter must know what's possible. / before going to the kitchen.
> "I want X" → waiter goes to kitchen
> → "we don't have X. / why is it on the menu?"
> → back to the table.
> Equivalent: I configured a host with port 8443 → that port isn't allowed → reconfigure from zero.
## Slide 10
STORY: Act 3 - Problem Anatomy
TENSION: High
EMPHASIS: Causa raiz y costo de fallar tarde
DELIVERY: Didactico, frases cortas
The Truth That Mutates
State is not static. / It can change at every step of the chain.
Step Truth for this actor
Guest speaks What they want
Waiter's notepad What was written down
Kitchen markings What's done / not done
Payment ticket What was actually served
> Fail early = fail cheap. / Fail in production = nightmare.
The context problem:
The waiter knows the regular customer: "always no salt."
The kitchen doesn't. / If the waiter changes. / that context disappears.
config drift is the same thing: Implicit state. / Not explicit. / Not propagated. / Lost silently.
The cost of failure depends on where it happens:
Fail at the table (impossible order): cheap. / caught before kitchen
Fail in kitchen (ingredient missing): medium. / renegotiate with guest
Fail at delivery (wrong dish arrives): expensive. / experience destroyed
## Slide 11
STORY: Act 3 - Problem Anatomy
TENSION: High
EMPHASIS: Causa raiz y costo de fallar tarde
DELIVERY: Didactico, frases cortas
"We Don't Have Mushrooms"
When an actor in the chain can't fulfill part of the order.
> "Can I substitute vegetables?"
> That renegotiation must be explicit. / Traced. / Re-authorized. / Not silent. / Not assumed.
config drift is silent renegotiation:
The system changes. / Nobody notified. / State diverges without trace.
Rust's answer. / Option :
[code example]
> The compiler check is the waiter who cannot pretend an ingredient exists.
## Slide 12
STORY: Act 3 - Problem Anatomy
TENSION: High
EMPHASIS: Causa raiz y costo de fallar tarde
DELIVERY: Didactico, frases cortas
The Config Evolution
How we got from code to YAML hell
Hardcoded. / everything inside the binary. / Full control. / Zero flexibility.
External config (JSON). / works between machines. / Unreadable for humans at scale.
YAML / TOML. / more readable. / Fragile syntax. / Implicit types. / Silent errors.
YAML + Serde. / Serde validates the structure:
Does the field exist? Is it the right type?
Do we accept "elephant" as a pet? If the type is String... / yes.
Serde validates shape. / Not meaning.
Helm / Jinja templates. / YAML generated from variables (in YAML).
Does it validate the content of the generated YAML? No. / Not at all.
Like using an LLM with a markdown reference: the format is there.
but is the content correct? Nobody safety rules that.
## Slide 13
STORY: Act 3 - Problem Anatomy
TENSION: High
EMPHASIS: Causa raiz y costo de fallar tarde
DELIVERY: Didactico, frases cortas
Continuous C I slash C D.
No semantic check.
Continuous hope.
(crossing our fingers in production)
🛡 ●○○○○ &nbsp;&nbsp; 😴 ○○○○○ &nbsp;&nbsp; 🔥 ●●●●●
## Slide 14
STORY: Act 3 - Problem Anatomy
TENSION: High
EMPHASIS: Causa raiz y costo de fallar tarde
DELIVERY: Didactico, frases cortas
Three Questions Without Answers
Question 1 —
— Why do we wait for things to break?
"Works on my machine". / in production. / I don't know
Fail late = maximum cost. / We want: fail fast. / fail cheap
Question 2 —
— Do we actually know what we want?
Is the declaration sufficient and consistent with what's possible?
What are the boundaries? Static or dynamic? What is the source of truth. / and when does it mutate?
Question 3 —
Can we guarantee determinism ?
C I slash C D without semantic check = continuous hope
We want certainty. / not randomness
"Works on my machine" cannot be the production standard
> We're not inventing anything new. / Everything already exists.
> The question is whether we're managing it correctly.
## Slide 15
STORY: Act 4 - Turning Point
TENSION: Peak
EMPHASIS: No faltan tools, falta paradigma
DELIVERY: Silencios intencionales
The tools weren't the problem.
The languages weren't the problem.
The approach was the problem.
## Slide 16
STORY: Act 4 - Turning Point
TENSION: Peak
EMPHASIS: No faltan tools, falta paradigma
DELIVERY: Silencios intencionales
Systems we don't know how to control.
We hope they work.
When they don't. / we fix them.
Continuous nightmare.
( alarm state as the new normal)
🛡 ●○○○○ &nbsp;&nbsp; 😴 ○○○○○ &nbsp;&nbsp; 🔥 ●●●●●
## Slide 17
STORY: Act 4 - Turning Point
TENSION: Peak
EMPHASIS: No faltan tools, falta paradigma
DELIVERY: Silencios intencionales
Rust
The answer to all three questions
## Slide 18
STORY: Act 4 - Turning Point
TENSION: Peak
EMPHASIS: No faltan tools, falta paradigma
DELIVERY: Silencios intencionales
The Bridge: From Serde to Types
Serde loads structurally valid config.
But "elephant" as pet: String compiles.
Rust's answer: don't use String. / Use a type.
[code example]
This is the shift.:
Not the config format. / The model of what it can contain.
Serde validates shape Types validate meaning
The compiler check validates before the binary exists.
## Slide 19
STORY: Act 5 - Resolution
TENSION: High -> Medium
EMPHASIS: Types y compiler como respuesta
DELIVERY: Claro y tecnico
What Rust Gives Us
Answer to Question 1: fail early. / fail cheap
[code example]
Answer to Question 2: explicit contracts
[code example]
## Slide 20
STORY: Act 5 - Resolution
TENSION: High -> Medium
EMPHASIS: Types y compiler como respuesta
DELIVERY: Claro y tecnico
The compiler check as Pre-Validator
Answer to Question 3:
guaranteed determinism
[code example]
The compiler check validates:
Before building the binary
Not after hours of execution
Not when a function nobody touched in months finally gets called
Predictable behavior: memory. / resources. / workflows
The compiler check is the waiter who validates the order.
Before it reaches the kitchen.
Before the guest waits.
Before any ingredient is missing.
🛡 ●●●●○ &nbsp; 😴 ●●●●○ &nbsp; 🔥 ●●○○○
## Slide 21
STORY: Act 5 - Resolution
TENSION: High -> Medium
EMPHASIS: Types y compiler como respuesta
DELIVERY: Claro y tecnico
The Human Impact
When the system is trustworthy:
✓ Sleep comes back
✓ Confidence returns
✓ The team trusts the automation
✓ Stress decreases
✓ You can actually rest
> What you can't measure: fear .
> What you can measure: MTTR .
> Before: > 30 minutes. / Now: .
🛡 ●●●●●&nbsp; 😴 ●●●●●&nbsp; 🔥 ●○○○○
## Slide 22
STORY: Act 5 - Resolution
TENSION: High -> Medium
EMPHASIS: Types y compiler como respuesta
DELIVERY: Claro y tecnico
Continuous C I slash C D.
Types. / compiler check. / Explicit state.
Continuous certainty.
(to keep sleeping well)
🛡 ●●●●● &nbsp;&nbsp; 😴 ●●●●● &nbsp;&nbsp; 🔥 ●○○○○
## Slide 23
STORY: Act 5 - Resolution
TENSION: High -> Medium
EMPHASIS: Types y compiler como respuesta
DELIVERY: Claro y tecnico
In Production
This is not theory
## Slide 24 (name: provisioning)
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
import { ref. / computed } from 'vue'
const useWhite = ref(false)
const bgStyle = computed(() => ({
backgroundImage: url('${useWhite.value ? "/w-arch-diag-v2.svg" : "/arch-diag-v2.svg"}').
backgroundSize: 'contain'.
backgroundRepeat: 'no-repeat'.
backgroundPosition: 'center'.
position: 'absolute'.
inset: '0'.
cursor: 'pointer'.
## Slide 25
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
Nickel
YAML rejected. / TOML rejected. / Reason: no type safety.
YAML wrote what we wanted.
It couldn't say what was possible.
Nickel closes that gap
— at config time. / not at 3 AM.
[code example]
Typed Source of Truth
Result (ADR-003): zero config type errors in production.
Config hierarchy:
defaults → workspace → profile → environment → runtime
Each layer merges.
Type system catches conflicts.
At config time. / not deployment time.
The guest wrote an impossible order.
Nickel makes impossible orders unwritable.
> Serde validates shape.
> Nickel validates meaning.
> The compiler check validates before deployment.
## Slide 26
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
Traits as Provider
The kitchen can change. / AWS ≠ UpCloud ≠ bare metal. / Same menu.
[code example]
Explicit state. / no drift:
[code example]
Contracts
Checkpoint every 5 minutes
No implicit state.
> No "the waiter remembers the customer doesn't want salt."
It's in the order. / Always. / Explicit.
## Slide 27
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
Dependency Graph
Failfast: bool is not a config option.
It's a principle encoded as a type.
Typed DAG. / dependency resolution enforced at workflow compile time:
The kitchen doesn't serve the main course before the starter is done.
DependencyType::Hard is that rule. / In the type system. / not in a runbook.
[code example]
Fail Fast. / Fail Cheap
DependencyType::Hard - failure stops the chain. / Always.
DependencyType::Soft - continues. / explicitly degraded.
DependencyType::Optional - missing is expected and fine.
> The compiler check catches the install order.
> Not the on-call engineer at 2 AM.
🛡 ●●●●● &nbsp; 😴 ●●●●● &nbsp; 🔥 ●○○○○
## Slide 28
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
Real Applications
## Kubernetes
The controller provisions cluster components as a typed workflow:
[code example]
Each dependency is a DependencyType.
The compiler check catches: installing Cilium without Kubernetes.
Not the on-call engineer at 2 AM.
>"In my machine it works" has a price here.
> This is the highest-stakes infra in the deck.
## Blockchain Validators
Validators require brutal uptime.
A validator that fails loses funds. / not your infra's money.
Your customer's.
Post-quantum cryptography: CRYSTALS-Kyber + Falcon + AES-256-GCM hybrid. / Validator keys protected against quantum computers.
SLOs with real error budgets: 99.99% = 52.6 min downtime/year. / Prometheus blocks deploys when burn rate exceeds budget.
Deterministic config: validator parameters are types. / A bondamount that isn't a valid u128 doesn't compile.
## Slide 29
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
Disaster Recovery
Rollback as a type. / not a procedure
3 AM. / Something broke. / You need to rollback.
Without types: you improvise.
With types: you choose a strategy
— or it doesn't compile.
[code example]
Multi-backend backup: restic. / borg. / tar. / rsync
— all as enum variants.
Production backup and DR restore use the same type. / the same schema.
> The runbook exists.
> Nobody reads it clearly at 3 AM under pressure.
> The type forces the decision before the crisis.
> The state is the same in prod and in DR. / Always.
## Slide 30
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
Self-Healing
> When something breaks at 3 AM
>. / the system responds. / not you.
[code example]
— Typed Remediation
What happens at 3 AM:
Alert fires → RemediationEngine matches condition → runs RestartService
Works: silent. / Nobody woken up.
Fails 3×: page sent. / with full state. / checkpoint. / and execution history.
> You wake up to information. / Not to chaos.
🛡 ●●●●● &nbsp; 😴 ●●●●● &nbsp; 🔥 ●○○○○
## Slide 31
STORY: Act 6 - Proof in Production
TENSION: Medium -> High
EMPHASIS: Casos reales e impacto operativo
DELIVERY: Energetico y concreto
Without types. / Without compiler check. / Without explicit state.
MTTR > 30 minutes.
Rust. / Types. / Explicit state.
Automated response.
MTTR (at 3 AM. / without you.)
🛡 ●●●●● &nbsp;&nbsp; 😴 ●●●●● &nbsp;&nbsp; 🔥 ●○○○○
## Slide 32
STORY: Act 7 - Close & CTA
TENSION: Medium -> Low
EMPHASIS: Cierre emocional con accion
DELIVERY: Lento y memorable
Why This Matters
For everyone in this room
## Slide 33
STORY: Act 7 - Close & CTA
TENSION: Medium -> Low
EMPHASIS: Cierre emocional con accion
DELIVERY: Lento y memorable
For You
If you've been frustrated like me
Rust solves problems you already have.
This is not hype. / It is operational relief.
Start here:
Model your infra as types
Let the compiler check pre-validate before deployment
If you're earlier in your career
Start with type safety from day one.
Build for stable work. / not only speed.
The shortest path:
Types for config.
Traits for providers.
Determinism for operations.
## Slide 34
STORY: Act 7 - Close & CTA
TENSION: Medium -> Low
EMPHASIS: Cierre emocional con accion
DELIVERY: Lento y memorable
I have perspective from long production experience.
I have seen technologies come and go.
Rust is not hype. / Rust is relief with evidence.
It solves real operational problems I had for decades.
More years is not a liability.
It is an advantage.
## Slide 35 (name: end)
STORY: Act 7 - Close & CTA
TENSION: Medium -> Low
EMPHASIS: Cierre emocional con accion
DELIVERY: Lento y memorable
Why I Needed Rust
Three Closing Lines
I have lived this problem for decades.
Rust gave me deterministic systems and better sleep.
Start small: model infra as types.
Thank you. / Questions?
More info:
· jesusperez.pro
· setup.systems · vapora.dev · rustelo.dev

Some files were not shown because too many files have changed in this diff Show More