docs: VISION/MARKET/FINDINGS, brand assets, CLAUDE directive refresh

Adds VISION.md (target personas + 20-credential surface), docs/MARKET.md
(competitive positioning), and docs/FINDINGS-2026-06-16.md (controlled-env
test run: live cutover POCs for postgres/gitea/aws/mysql/redis/wireguard, the
backup-gate defect + fix, and the MariaDB unprivileged-rotation gap). Swaps the
placeholder _igo_options assets for the banner/icon set. Refreshes the project
CLAUDE.md directive.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
leetcrypt
2026-06-18 14:48:56 -07:00
parent 12192a8423
commit f6d145669c
23 changed files with 800 additions and 96 deletions
+24 -5
View File
@@ -3,9 +3,27 @@
**incredigo** is a local-first, encrypted, RAM-only credential custody tool (Go). **incredigo** is a local-first, encrypted, RAM-only credential custody tool (Go).
The folder is still named `credrot/`; the module/binary are `incredigo`. The folder is still named `credrot/`; the module/binary are `incredigo`.
> Read this first, then [`DESIGN.md`](DESIGN.md) (architecture + threat model) and — > Read this first, then [`VISION.md`](VISION.md) (who it's for + the mission),
> if present locally — `docs/ROTATION.md` (rotation design; **gitignored**, shared > [`DESIGN.md`](DESIGN.md) (architecture + threat model), [`docs/MARKET.md`](docs/MARKET.md)
> individually, not on the upstream repo). > (competitive landscape + positioning) and — if present locally — `docs/ROTATION.md`
> (rotation design; **gitignored**, shared individually, not on the upstream repo).
## Vision — catch vibe-coding security disasters before they happen
The primary user is the **vibe coder**: the fast-moving AI-assisted builder (OpenClaw / Hermes /
Claude-Code / Cursor) who ships in a weekend and accumulates dozens of credentials —
`.env` keys, `sk_live`, `DATABASE_URL`, GitHub PATs, AWS keys, Supabase `service_role` — each
**generated once, pasted in, and never rotated**. incredigo finds that sprawl on the machine,
custodies it in RAM, backs it up, **rotates what's scriptable**, and **guides the human through
the rest** — before a forgotten key becomes a breach. **Secondary user:** the non-technical
password reuser (weak/reused passwords saved in the browser) — same engine, friendlier face, via
the browser worklist + change-URL links.
Tailor every feature to those two. **Do NOT build toward enterprise PAM / machine-identity-at-
scale / compliance-cert / SaaS-hub** territory — that's the incumbents' moat. The target
credential surface (the driver + scanner roadmap) is the **20-credential list in `VISION.md`**.
Full crystallized vision in [`VISION.md`](VISION.md); competitive rationale in
[`docs/MARKET.md`](docs/MARKET.md).
## North-star goal ## North-star goal
@@ -104,5 +122,6 @@ on top (do not put an LLM in the deterministic destructive hot path).
- Commits: end messages with the `Co-Authored-By: Claude …` trailer. Push only when - Commits: end messages with the `Co-Authored-By: Claude …` trailer. Push only when
asked. Remote is **gitea only, never GitHub** asked. Remote is **gitea only, never GitHub**
(`https://100.117.177.50:3030/trill-technician/incredigo`). (`https://100.117.177.50:3030/trill-technician/incredigo`).
- Design docs (`docs/ROTATION.md`) are kept local/gitignored by request; `DESIGN.md`, - Design docs (`docs/ROTATION.md`) are kept local/gitignored by request; `VISION.md`,
`README.md`, `docs/SECURITY.md`, `docs/ADDING_A_SCANNER.md` are tracked. `DESIGN.md`, `README.md`, `docs/MARKET.md`, `docs/SECURITY.md`, `docs/ADDING_A_SCANNER.md`
are tracked.
+143
View File
@@ -0,0 +1,143 @@
# incredigo — Vision
## Catch vibe-coding security disasters before they happen.
You build an app in a weekend with Claude, OpenClaw, Hermes, Cursor. By Sunday night you've
accumulated three dozen credentials — an Anthropic key, a Stripe `sk_live`, a `DATABASE_URL`, a
GitHub PAT, an AWS access key, a Supabase `service_role` — each **generated once, pasted into a
`.env`, and forgotten.** Maybe it got committed. Maybe it's in your shell history. Maybe it's in
eight places. You'll "deal with security later."
**incredigo is the safety net for later-that-never-comes.** It finds every credential sprawled
across your machine, locks them in a RAM-only vault, takes a verified backup, **rotates the ones
a script can rotate**, and hands you a **guided, ranked checklist** for the ones that need a human
— all local-first: no cloud, no server, no SaaS, one binary. The forgotten `sk_live` gets rotated
*before* it becomes a breach disclosure.
> **One line:** *The local-first credential rotator that saves fast-moving AI-assisted builders
> from the security debt they're accumulating in real time.*
---
## The problem
The "vibe coder" — the AI-assisted builder who ships fast and worries about security after the
fact — is the fastest-growing developer cohort, and the one accruing credential risk fastest:
- **Secrets sprawl is exploding.** GitGuardian's *State of Secrets Sprawl 2026*: **29M new
hardcoded secrets in 2025, +34% YoY**, with each live secret appearing in **~8 locations on
the same machine** (`.env`, shell history, IDE configs, cached tokens, build artifacts).
- **Generate-once, rotate-never.** AI codegen mints keys on demand and pastes them inline. They
are rarely scoped, rarely rotated, often duplicated, and frequently committed.
- **No tool serves the cleanup.** Enterprise PAM/secrets platforms (CyberArk, Vault, Doppler)
rotate *what already lives in their hub*; they don't go discover the mess on your laptop. The
one consumer attempt at automating the long tail (Dashlane) gave up as "too brittle."
That gap — **discover the sprawl on this machine, then rotate it, self-custody, no cloud** — is
incredigo's reason to exist. (Full competitive analysis: [`docs/MARKET.md`](docs/MARKET.md).)
---
## Who this is for
**Primary — the security-literate-enough vibe coder.** OpenClaw / Hermes / Claude-Code / Cursor
builders who move fast and accumulate credentials across `.env`, `~/.aws`, `~/.netrc`, `~/.ssh`,
`~/.docker`, `~/.kube`, `docker-compose.yml`, plus a gopass store. They distrust (or can't legally
use) a SaaS secret hub, live on the CLI, and want **headless rotation in CI *and* an interactive
TUI**. Job-to-be-done: *"Find every credential I have, back them up, rotate what's automatable,
and give me a clean checklist for the rest — without anything leaving my machine."*
**Secondary — the non-technical password reuser.** People who never change their passwords, reuse
weak ones, and let the browser remember everything. The *same engine* (discover → back up →
rotate / guide), with a friendlier face: point it at the browser's saved-login store, and it
produces a ranked, deduplicated worklist with a direct **change-password link** per site — never a
plaintext password dump, never bypassing MFA. The bridge from "developer tool" to "anyone with a
password problem."
---
## The 20 credentials a vibe coder accumulates
The target surface. Each maps to how incredigo handles it: **[DB]** in-place database rotation
driver · **[API]** provider-API rotation driver · **[LOCAL]** self-generated secret incredigo can
regenerate + propagate · **[GUIDE]** no rotation API → guided worklist + change-URL (human in the
loop, never bypassing MFA/CAPTCHA).
| # | Credential | Where it lives | Why it bites vibe coders | Handling |
|---|---|---|---|---|
| 1 | **LLM API keys** (Anthropic, OpenAI, Gemini, Groq, OpenRouter, Mistral, HuggingFace) | `.env`, shell rc, `~/.config` | The defining vibe-coder key; minted constantly, pasted inline, billed by usage | GUIDE → API |
| 2 | **AI infra keys** (Pinecone/Qdrant/Weaviate, Replicate/Fal/ElevenLabs/Stability) | `.env` | Spun up per experiment, never cleaned up | GUIDE |
| 3 | **MCP / agent-gateway tokens** (OpenClaw/Hermes/Claude MCP server configs) | `~/.claude.json`, MCP config files | Grant broad tool/host access; rarely scoped or rotated | GUIDE / LOCAL |
| 4 | **AWS access keys** | `~/.aws/credentials`, `.env` | Long-lived, over-privileged, leak to GitHub constantly | **API (IAM)** |
| 5 | **GCP service-account JSON** | `~/.config/gcloud`, `.env` | Whole-key files copied around | API |
| 6 | **Cloudflare API token / global key** | `.env`, `~/.cloudflared` | Global key = account-wide; pasted into scripts | GUIDE / API |
| 7 | **Deploy-platform tokens** (Vercel, Netlify, Railway, Render, Fly.io) | `.env`, `~/.vercel` | One token = full deploy control | GUIDE / API |
| 8 | **Postgres DSN** | `.env` `DATABASE_URL`, `docker-compose.yml` | Default/weak passwords, reused across envs | **DB ✅** |
| 9 | **MySQL / MariaDB DSN** | same | same | DB |
| 10 | **Redis URL / password** | `.env`, compose | Often no password at all | DB |
| 11 | **Managed-DB keys** (Supabase `service_role`, Neon, PlanetScale, Mongo Atlas) | `.env` | `service_role` bypasses row-level security — catastrophic if leaked | GUIDE / API |
| 12 | **Git platform PATs** (GitHub, GitLab, Gitea, Bitbucket) | `~/.netrc`, `~/.config/gh`, git credential store | Broad-scope `repo` tokens; rarely expire | **API** |
| 13 | **Package-registry tokens** (npm `~/.npmrc`, PyPI, crates.io) | home configs | Publish rights → supply-chain risk | GUIDE |
| 14 | **Container-registry creds** (Docker Hub, GHCR) | `~/.docker/config.json` | Base64 "auth" mistaken for encryption | GUIDE |
| 15 | **CI/CD secrets** (GitHub Actions secrets, workflow env) | repo settings, `.github/workflows` | 59% of leaked-secret machines are CI runners | API / GUIDE |
| 16 | **SSH private keys / deploy keys** | `~/.ssh/id_*` | Unencrypted keys, shared deploy keys, never rotated | **driver: regen + `authorized_keys`** |
| 17 | **Kubeconfig tokens / client certs** | `~/.kube/config` | Cluster-admin creds in a dotfile | GUIDE |
| 18 | **Payment / auth / messaging keys** (Stripe `sk_live`, Clerk/Auth0/Firebase, Twilio, SendGrid/Resend, Slack/Discord, webhook signing secrets) | `.env` | Money + identity + spam blast radius | GUIDE / API |
| 19 | **App signing / session secrets** (Django `SECRET_KEY`, NextAuth secret, Rails `master.key`, JWT secrets) | `.env`, config | Self-generated randoms — easy to rotate, easy to forget | **LOCAL: regenerate + propagate** |
| 20 | **Browser-saved passwords + manager exports** | Chrome/Firefox login DB, CSV | Reused, weak, never changed (the secondary market) | **GUIDE: worklist + change-URL** |
This list is the **driver-ladder roadmap** and the **scanner roadmap** at once: every `[DB]`/`[API]`/
`[LOCAL]` row is a future one-file `Rotator`; every row is a discovery surface.
### Secondary-persona extension: network credentials (VPN + router)
The 20 above are the **primary (vibe-coder)** surface and remain the centre of gravity. Two
adjacent credential classes serve the **secondary persona** ("anyone with a password problem")
and are deliberately added — they reuse the same safe spine, not a new product:
- **VPN keys/credentials.** *Self-hosted WireGuard* rotates exactly like SSH (`[LOCAL]`:
regenerate the keypair in RAM, propagate the new public key to the peer, verify the handshake,
drop the old). *Mullvad* is the one mainstream consumer VPN with a real device-key rotation API
(`[API]`, account-number auth). Commercial account passwords (Nord/Express/Proton) have no
rotation API → `[GUIDE]`.
- **Router admin password.** *OpenWrt* (and other SSH/`ubus`-exposing prosumer firmware) is
programmatically rotatable (`[API/LOCAL]` via `uci`/`ubus`). Generic consumer routers
(TP-Link/Netgear/Asus web-UI) have no stable API → `[GUIDE]` / browser long-tail.
These stay within the non-goals below (self-owned only, never an attack tool, never bypass
MFA/CAPTCHA); they do **not** pull incredigo toward enterprise network/PAM management.
---
## How incredigo answers it
The same safe spine for every credential, automatable or not:
1. **Discover** — scan the machine's credential surface (`.env`, `~/.aws`, `~/.netrc`, `~/.ssh`,
`~/.docker`, `~/.kube`, git, `--path`, browser export).
2. **Custody** — load secrets into a RAM-only memguard vault (mlock/DONTDUMP); plaintext never
hits disk.
3. **Backup (mandatory)** — a sealed, **verified** snapshot before anything changes. No backup,
no rotation. Non-negotiable.
4. **Rotate** — drivers change the automatable ones in place (verify-new-before-revoke-old), and
close the blast radius by guiding consumer updates (`.env`, CI vars, k8s).
5. **Guide** — for the no-API long tail, emit a ranked, deduplicated worklist with per-site
change-password links; the human acts, MFA/CAPTCHA are never bypassed.
Headless for CI (`$INCREDIGO_PASSPHRASE`, no TTY) or interactive via the TUI. The PGP passphrase
is the single root of trust — back up the key, use a strong passphrase.
---
## Non-goals
- **Not enterprise PAM.** No fight with CyberArk/Vault/Delinea over machine-identity-at-scale,
compliance certifications, or a managed cloud service. (See `docs/MARKET.md`.)
- **Not a SaaS secret hub.** No server, no daemon, no telemetry, nothing leaves the machine.
- **Not an attack tool.** Self-owned accounts only, with the owner's authorization. Never resembles
credential stuffing or account takeover; never bypasses MFA/CAPTCHA — it hands off to the human.
- **Not "auto-change everything."** Fully-silent browser rotation is a known tar pit (Dashlane).
incredigo rotates what's scriptable and *guides* the rest.
See [`DESIGN.md`](DESIGN.md) for architecture + threat model and [`docs/MARKET.md`](docs/MARKET.md)
for the competitive landscape.
+280
View File
@@ -0,0 +1,280 @@
# Findings — controlled-environment test run (2026-06-16)
Source: `~/incredigo-lab-stage/lab-harness.sh` (fresh isolated gopass store + fake,
canary-tagged inputs for all 8 scanners). FAKE creds only; never touches a real store.
## Live rotation POC — first real driver (postgres), 2026-06-16
**Result: real end-to-end cutover proven** in the `incredigo-sbx` Multipass VM (real
gopasspw/gopass 1.15.16, real PostgreSQL 16, isolated `GOPASS_HOMEDIR`/`GNUPGHOME`,
FAKE `labapp@labdb` role).
Flow exercised by the shipped binary via `incredigo rotate --execute`
(gated: `INCREDIGO_ALLOW_EXECUTE=1` + `--execute`):
1. **Backup gate** sealed + verified 1 entry → `~/.incredigo/rotation-backups/<ts>.age` before any change (Hard Rule #1).
2. **Rotate** ran `ALTER USER labapp WITH PASSWORD '<48-hex>'` over the OLD connection.
3. **Verify(new)**`SELECT 1` with the new secret passed.
4. **Store** the rebuilt DSN back into gopass at the same entry; **re-read + Verify(stored)** passed.
5. **RevokeOld** = no-op (in-place ALTER is itself the cutover).
Cutover assertions (all passed):
- OLD password (`oldpw835`) **rejected** after rotation — `FATAL: password authentication failed`.
- NEW 24-byte hex password (read back from gopass) **authenticates**.
- Audit log (`rotate-execute`) contains **no password substring** (old or new).
Spine: `internal/rotate/execute.go` (`Execute`), driver `internal/rotate/postgres.go`
(self-registers via `init`), sourcing `cmd/incredigo` `gopassCredsForExecute`
(Mode A — reconstructs `Source` from the gopass path segment), persistence
`sink.Gopass.Show`/`InsertAt`. Unit proof of the same cutover (no live DB):
`internal/rotate/postgres_test.go:TestPostgresRotateRealCutover`. Full suite: 45 green.
## Live rotation POC — gitea PAT driver (provider-API pattern), 2026-06-17
**Result: real create-new → verify → revoke-old cutover proven** against a real
Gitea 1.25.0 (sqlite, http on loopback) inside the `incredigo-sbx` VM, isolated
gopass store, self-owned `labadmin` account. This is the **first provider-API
driver** and the first whose `RevokeOld` is a real destructive API call.
Provision: `~/incredigo-lab-stage/lab-provision-gitea.sh` (stands up Gitea, mints a
seed token via basic auth, seeds the driver-ready blob into gopass). Flow exercised
by the shipped binary via `incredigo rotate --execute` (gated):
1. **Backup gate** sealed + verified 1 entry before any change (Hard Rule #1).
2. **Rotate** `POST /api/v1/users/labadmin/tokens` (basic auth) → new token.
3. **Verify(new)** `GET /api/v1/user` with the new token → 200, `login=labadmin`.
4. **Store** the rebuilt blob back into gopass; **re-read + Verify(stored)** passed.
5. **RevokeOld** `DELETE …/tokens/<old-name>` — a real token deletion.
Cutover assertions (all passed):
- OLD token → `GET /user` returns **401** after rotation (revoked).
- NEW token (read back from gopass) → **200**, `login=labadmin`.
- Gitea token list now shows only `incredigo-rotated-<ts>`; the seed token is gone.
- Audit log (`rotate-execute`) contains **no token substring** (old or new); old ≠ new.
Spine: `internal/rotate/gitea.go` (single-line `https://user:token@host/?name=&pw=`
blob so it round-trips the existing single-line gopass sink; `authManage` prefers
basic auth when a mgmt password is present, else token auth). Discovery for the
gitea PAT type: `internal/discover/tea.go` (Gitea `tea` CLI config → driver-ready
blob, Source `gitea`). Unit proof of the same enforced cutover (httptest Gitea
emulator that 401s a revoked token): `internal/rotate/gitea_test.go`.
## SSH key driver (unit-proven cutover), 2026-06-17
`internal/rotate/sshkey.go` mints a new ed25519 keypair **entirely in memory**
(golang.org/x/crypto/ssh; private key never touches disk — Hard Rule #3), installs
only the new PUBLIC key into `authorized_keys`, verifies by SSH-dialing the
authority, and `RevokeOld` removes the old public key (atomic rewrite). Proven by an
**in-process SSH server** test: old key rejected after revoke, new key works, no
`PRIVATE KEY` material in `authorized_keys` (`internal/rotate/sshkey_test.go`).
Discovery broadened (`internal/discover/ssh.go`): content-based key detection (not
just `id_*`) + `~/.ssh/config` `IdentityFile` parsing. Live VM POC not yet run.
Full suite after all three drivers: **50 green**, `-race` clean on
rotate/sink/vault/discover.
## Live rotation POC — AWS IAM access key driver (cloud-key pattern), 2026-06-17
**Result: real create-new → verify → revoke-old cutover proven** against a
self-hosted **moto 5.2.2** mock AWS (`moto_server` on http://127.0.0.1:5000) inside
the `incredigo-sbx` VM, isolated gopass store, self-created IAM user `labapp`. This
is the cleanest provider-API driver: the OLD credential **self-identifies** — an
access key's `AccessKeyId` is non-secret and rides in the secret blob, so RevokeOld
deletes exactly the right key (no name-capture dance Gitea needed).
Implementation note: the driver speaks the AWS **Query protocol** (form-POST
`Action=…&Version=…`) signed with **hand-rolled SigV4** (`crypto/hmac`) — zero new
go.mod deps, keeping the static binary lean (the AWS SDK v2 would have added ~50
indirect deps). Spine: `internal/rotate/aws.go`. Secret blob is the single line
`aws://<AccessKeyId>:<SecretAccessKey>@aws/?region=<r>[&endpoint=<url>]` so it
round-trips the single-line gopass sink; `endpoint=` overrides the API base for
moto/LocalStack, absent → real `iam/sts.amazonaws.com`. Discovery broadened:
`internal/discover/aws.go` now emits that driver-ready blob (was the bare secret) and
resolves region from env / `~/.aws/config`.
Flow exercised by the shipped binary via `incredigo rotate --execute` (gated):
1. **Backup gate** sealed + verified 1 entry before any change (Hard Rule #1).
2. **Rotate** IAM `CreateAccessKey` (no UserName → moto resolves the calling
identity from the signing key) → new key pair.
3. **Verify(new)** STS `GetCallerIdentity` with the new key → ARN returned.
4. **Store** the rebuilt blob into gopass; **re-read + Verify(stored)** passed.
5. **RevokeOld** IAM `DeleteAccessKey` targeting the OLD `AccessKeyId` — a real delete.
Cutover assertions (all passed, `lab-verify-aws.sh`):
- After rotate, IAM `ListAccessKeys(labapp)` shows **only** the new key; the old
`AccessKeyId` is **gone** at the provider. old ≠ new.
- New key (read back from gopass) authenticates; audit log (`rotate-execute`) carries
**no AccessKeyId and no SecretAccessKey**.
**moto enforcement caveat (investigated, important).** moto's STS
`GetCallerIdentity` does **not** validate access-key existence — a *deleted* key
still returns a (generic `user/moto`) ARN rather than a 403. So against moto, the
driver's STS-based Verify cannot itself prove "old key dead"; the live cutover proof
is therefore **IAM `ListAccessKeys`** (ground truth that the delete took effect),
asserted in `lab-verify-aws.sh`. The *enforced* 403-on-revoked-key path (what real
AWS does) is proven instead by the **unit test**: `internal/rotate/aws_test.go`'s
`awsEmu` reads the `AccessKeyId` from the SigV4 `Credential=` header and rejects any
key not currently present, so `TestAWSRotateRealCutover` asserts the old key returns
403 after revoke, the new key still authenticates, and old ≠ new. The driver's Verify
(plain "ARN non-empty") is correct for real AWS, which **does** 403 an invalid key;
no moto-specific leniency was baked into the driver.
Provision: `~/incredigo-lab-stage/lab-provision-aws.sh` (starts moto, creates
`labapp` + seed key via boto3, seeds the driver-ready blob into isolated gopass);
verify: `~/incredigo-lab-stage/lab-verify-aws.sh`. Unit proofs:
`TestAWSRotateRealCutover` (enforced cutover), `TestAWSSecretRoundTrip` (blob carries
`/`+`=` secrets intact), `TestSigV4HeadersStable` (Authorization shape).
Full suite after four drivers: **53 green**, `-race` clean on
rotate/sink/vault/discover.
## VPN / router / DB-clone batch — 5 drivers, 3 live cutovers, 2026-06-18
Approved expansion (VPN + router secondary persona, plus Tier-1 DB clones). Five new
one-file drivers, each with a table-driven unit test (cutover proof + leak-check). Three
were additionally proven **live** in the `incredigo-sbx` VM against real services; one is
unit-proven with a live QEMU POC deferred; one is unit-only by design.
| Driver | Pattern | Live POC | Proof |
|--------|---------|----------|-------|
| `mysql` | in-place DB password | **PASS** (real MariaDB) | `lab-provision-dbclones.sh` |
| `redis` | in-place `CONFIG SET requirepass` | **PASS** (real redis-server) | `lab-provision-dbclones.sh` |
| `wireguard` | local keypair regen + peer propagate | **PASS** (real `wg`) | `lab-provision-wg.sh` |
| `openwrt` | in-place SSH `passwd` | deferred (QEMU on 1-CPU VM) | unit: in-process SSH server |
| `mullvad` | provider-API device key | n/a (needs paid acct) | unit: httptest TLS emulator |
**MySQL + Redis live cutover (`lab-provision-dbclones.sh`, all assertions PASS).** Real
`mariadb-server` + `redis-server`, isolated gopass, fake `labapp@labdb` and a fake redis
`requirepass`. Backup gate sealed+verified first; `rotate --execute` flipped both; the
OLD password is **rejected** and the NEW one (read back via `gopass show -o`)
**authenticates** for each engine.
> **Driver gap found & fixed — MariaDB unprivileged self-rotation.** MariaDB rejects
> `ALTER USER CURRENT_USER() IDENTIFIED BY …` for a non-admin (ERROR 1227 — it demands the
> global `CREATE USER` privilege even for one's *own* account), which is exactly the
> target persona's situation (a vibe coder's DB user is scoped to one schema with
> `GRANT ALL ON app.*`). The MySQL-only `USER()` spelling also fails on MariaDB (ERROR
> 1064). Fix (`internal/rotate/mysql.go`): try a fallback chain — `ALTER USER
> CURRENT_USER()` (MySQL self-service) → `SET PASSWORD = PASSWORD('…')` (MariaDB / MySQL
> 5.7) → `SET PASSWORD = '…'` (MySQL 8 / MariaDB ≥10.4); first to succeed wins. After the
> fix the live POC passed on MariaDB.
**WireGuard live cutover (`lab-provision-wg.sh`, all assertions PASS).** Real
`wireguard-tools`: `wg0` = ours, `wg1` = the peer that trusts us. Seeds `wg0`'s old
private key into isolated gopass, `rotate --execute` mints a new clamped curve25519
keypair, applies the new private key to `wg0` (via stdin → `wg set wg0 private-key
/dev/stdin`, never argv), and re-points the peer to the new public key (carrying over
allowed-ips). Assertions: private key changed; peer trusts NEW pubkey; peer **dropped**
OLD pubkey; and the headline compatibility proof — **`wg0`'s live interface public key
equals the public key our Go code derived**, i.e. our in-process curve25519 derivation
matches real `wg pubkey`.
> **POC scripting gotcha (Mode A prefix).** Mode A reconstructs the driver `Source` from
> the **first** path segment after `--prefix` (`imported/<source>/<slug>`). The WG POC
> first ran with `--prefix imported/wireguard/`, which made `Source="wg0"` (no driver
> Detected it → ROTATED 0). Fix: point `--prefix` at the `imported/` **root** so
> `imported/wireguard/wg0` resolves `Source="wireguard"`. This is a harness fix, not a
> driver bug, but worth recording: narrowing the prefix below the source dir breaks
> source recovery.
**OpenWrt (unit-proven, live deferred).** `internal/rotate/openwrt.go` dials the router
with the OLD password and runs BusyBox `passwd`, feeding the new password twice on the
SSH-encrypted stdin (never argv); setting the password IS the cutover (RevokeOld no-op).
Proven by an **in-process SSH server** (`openwrt_test.go`) with a mutable current
password: old password stops authenticating after rotate, new works, no secret in argv or
error strings. A live OpenWrt-in-QEMU POC is the heaviest (full-system emulation on the
1-CPU sandbox VM) and is **deferred**.
**Mullvad (unit-only by design).** `internal/rotate/mullvad.go` is the provider-API
device-key pattern: fetch an access token from the account number, `POST
/accounts/v1/devices {pubkey}` to register a freshly-derived WireGuard key, verify the
device lists that exact pubkey, then `DELETE` the old device. Proven against an httptest
**TLS** emulator (`mullvad_test.go`) that inspects every request body for the private key
(never sent) and 404s a deleted device. No live POC — that needs a real paid Mullvad
account; the API contract is faithfully emulated instead.
**Discovery broadened (Hard-rule standing directive — dynamic discovery per type).** The
env scanner (`internal/discover/env.go`) now tags DB connection URLs by scheme:
`postgres`/`postgresql``postgres`, `mysql`/`mariadb``mysql`, `redis`/`rediss`
`redis` (requires a `user:password` authority), so `DATABASE_URL`/`REDIS_URL`/`MYSQL_URL`
in a `.env` route straight to the matching in-place driver, kept regardless of the
secret-name heuristic. Regression: `scanners_test.go:TestEnvScanner`.
Full suite after the batch: **68 green**, `-race` clean on rotate/sink/vault/discover.
## Summary
- Harness result: **31/31 PASS**, including the global secret-leak canary (no CANARY
token or `PRIVATE KEY` material reached any command output).
- One **real defect** found by manual triage of the backup gate, since fixed.
## Defect 1 — backup gate silently under-covered dot-segment entries (FIXED)
**Severity:** high — a silent violation of Hard Rule #1 (backup before rotate).
**Symptom.** After `migrate`, the store held 10 imported credentials, but the rotate
dry-run backup gate sealed/verified only **7**. The 3 missing were the `.env`-derived
entries: `imported/env/.env/{api_token,database_url,sendgrid_api_key}`.
**Root cause.** `internal/sink/gopass.go:slug()` lower-cased and space-collapsed the
credential Identity but **kept leading dots**. An env credential has
`Identity = ".env / API_TOKEN"`, so `StorePath` became
`imported/env/.env/api_token`. The `.env` path segment is dot-prefixed, and gopass
**hides dot-prefixed directories/leaves from `ls --flat`**. `bundle.ListPaths` (and the
backup gate that consumes it) enumerate via `ls --flat`, so those 3 entries were
invisible — never sealed, never verified.
**Why it was silent.** The gate's integrity check compares *sealed == verified* counts.
Both were 7, so 7==7 passed. The gate had no notion of *expected* count, so
under-coverage produced no error.
**Fix.** `slug()` now strips leading dots per `/`-separated path segment, so
`.env``env` (`imported/env/env/api_token`). Entries are now enumerable and the gate
covers all of them. Regression test: `internal/sink/bundle_test.go:TestStorePathNoDotSegment`
asserts no produced StorePath contains a dot-prefixed segment. Full suite: 42 tests
green, `-race` clean on `sink`/`rotate`.
## Root cause of the *lapse* (why it stayed silent) and prevention
The slug bug was the trigger, but it stayed silent because of a deeper flaw in the gate:
`rotate.Snapshot` asserts `verified == res.n`, and **both counts descend from the same
`gopass ls --flat` enumeration** (`ExportTo``ListPaths`, bundle.go). A blind spot in
that enumeration is inherited by both sides, so the comparison can never catch it. The
check proves *round-trip integrity* (the bundle re-reads to the same count it sealed), not
*coverage* (the bundle holds every credential in the store). **A safety gate that compares
two values derived from the same source cannot detect an error in that source.**
Prevention, layered:
1. **Source fix (done).** `slug()` strips leading dots → incredigo can no longer write any
path that `ls --flat` hides. `TestStorePathNoDotSegment` locks it in. This removes the
only trigger incredigo controls and is backend-agnostic.
2. **Independent coverage cross-check (done, harness).** `lab-harness.sh` T8b counts the
on-disk `*.gpg` files under `imported/` — ground truth that does **not** go through
`ls --flat` — and asserts the gate sealed exactly that many. This is where an
independent count is cheap and reliable (`GOPASS_HOMEDIR` is known/controlled). It would
have failed loudly on the original 7-vs-10.
**Production gate — deliberately *not* changed.** Adding the same on-disk `.gpg` walk to
`rotate.Snapshot` would couple the safety-critical gate to gopass's fs-backend layout
(store-dir resolution varies by gopass version/mount and breaks for non-fs backends). That
trades one fragility for another and could make the gate *falsely* abort or pass. The
two-layer defense above (write-side invariant + harness cross-check) closes the class
without that coupling. If we later want an in-gate coverage assertion, the clean path is a
backend-agnostic enumeration primitive in `sink` (not an fs-layout assumption) — a separate
design task.
## ICM evaluation (does the methodology help here?)
**Recommendation: do not adopt ICM for the incredigo codebase now.** ICM
(`icm-scaffold` skill) structures an *LLM-orchestrated* workflow as numbered
markdown stages — "folders are the program, one agent reads the right file at the
right moment." incredigo is a deterministic **compiled Go tool** with a package
architecture, an interface/registry extension model, and a real test suite. ICM's
folders-as-program model does not map onto a binary, and the project directive
explicitly bans an LLM in the destructive hot path. Adopting ICM here would add
scaffolding with no control-flow benefit. The one place it *could* fit later is the
**AI-router / browser long-tail** rotation layer (ROTATION.md step 4), which is by
design a skill/MCP layer *on top* of the Go core — revisit ICM only when that layer
is built.
+231
View File
@@ -0,0 +1,231 @@
# incredigo — Market Analysis & Positioning (2026-06-16)
Research into the credential/secret **rotation** market: who sells it, who buys it, where
the white space is, and who incredigo should be built for. Sourced web research (20252026);
treat market-size figures as analyst-dependent and the academic adoption numbers as
directional (see Caveats).
---
## TL;DR
- The **money** is in enterprise **PAM** (~$4.35.6B, ~22% CAGR) and **machine-identity**
rotation — and it is consolidating fast: **CyberArk → Palo Alto Networks (~$25B, Jul 2025)**,
**HashiCorp Vault → IBM ($6.4B, Feb 2025)**. That is not a fight a local-first CLI wins.
- **Three credible white-space wedges incredigo already targets:**
1. **Local-file secret discovery + rotation** for solo devs / small teams (sprawl is
exploding: **+34% YoY**, secrets appear in **~8 places per machine**). Incumbents assume
you've *already* migrated into their hub — nobody owns "find the mess on this laptop, then
rotate it." → incredigo **Mode B**.
2. **Local-first / RAM-only / no-cloud / no-server.** OpenBao/Infisical/Bitwarden prove the
no-cloud demand, but every one is a **server/daemon**. A **single RAM-only binary** is
genuinely differentiated.
3. **The manual/browser "long tail"** of accounts with no rotation API. Every incumbent —
enterprise PAM *and* consumer password managers — explicitly refuses this. The one serious
consumer attempt (**Dashlane**) **publicly gave up** as "too brittle." → incredigo's
**guided worklist + change-URL** layer.
- **RFC 8615 `.well-known/change-password` is a thin primitive** (~12% top-site adoption, often
unhelpful redirects). It confirms "auto-change everything" is unrealistic and validates
incredigo's split: **rotate what's automatable, guide the human through the rest.**
---
## 1. Vendors — who sells rotation, and to whom
### Enterprise secrets platforms
| Vendor | Rotation story | Pricing | Typical client |
|---|---|---|---|
| **HashiCorp Vault** (now **IBM**, $6.4B, closed Feb 27 2025) | Core = **dynamic short-lived secrets** + static rotation. Moved to **BSL** license (2023) → OpenBao fork. **HCP Vault Secrets SaaS sunset** (EOS Jun 30 2025, EOL Jul 1 2026). | Community Edition free (self-host); HCP Vault Dedicated ~$1.58/hr | Large enterprise, platform/DevOps |
| **CyberArk** (incl. **Conjur**) — **being acquired by Palo Alto Networks (~$25B, announced Jul 30 2025)** | PAM market leader; automated rotation + deep audit. Conjur = machine/DevOps secrets. | Conjur OSS free; enterprise ~$1,0001,500 / identity / yr | Large/regulated enterprise |
| **Akeyless** | Vaultless SaaS, JIT access + automated rotation ("Distributed Fragments Cryptography") | Custom / contact sales | Mid-large enterprise (**78% of revenue from public corps**, 2 Fortune-5) |
| **Doppler** | Fully automated rotation, broad integrations (AWS/GCP/CI/KMS) | From **$3/user/mo** | Dev/DevOps teams, startup→midmarket |
| **Infisical** (OSS, MIT) | Rotation for Postgres/MySQL/AWS IAM; **flagged for limited retry/failure guidance** | Cloud from $8/user/mo; self-host free | Developers, OSS-leaning, self-host shops |
| **Phase** | Automated rotation + keeps deployments in sync; cloud or self-host | — | Dev/DevOps, compliance-driven |
### Cloud-native (rotation = "you write the Lambda/Function")
| Vendor | Rotation story | Pricing |
|---|---|---|
| **AWS Secrets Manager** | Native rotation for **RDS/Redshift/DocumentDB only**; others need custom Lambda. re:Invent 2025: "zero-touch rotation" for some 3rd-party secrets. | $0.40/secret/mo + $0.05/10k calls |
| **Azure Key Vault** | Microsoft-centric; FIPS/gov favored | $0.03/10k ops |
| **GCP Secret Manager** | Rotation = schedule → Pub/Sub → **Cloud Function you author** | $0.06/secret version/mo |
### PAM specialists
- **Delinea** — PAM + password rotation, **mid-market** (fast deploy, low TCO); 2024 Gartner MQ Leader.
- **BeyondTrust** — PAM + credential rotation, **enterprise**; Gartner MQ Leader ×6, 2025 Forrester Wave Leader (PIM).
- **Teleport** (w/ StrongDM, HashiCorp Boundary) — **certificate/ephemeral-access**, cloud-native/DevOps; ephemeral certs reduce the *need* to rotate long-lived secrets.
### Password managers w/ rotation crossover
- **1Password** — Business $7.99/user/mo; Secrets Automation via self-hosted Connect Server.
- **Bitwarden** — OSS, client-side crypto, self-hostable; Secrets Manager for devs; business from $4/user/mo; $100M Series B (2022).
- **Keeper** — KeeperPAM: automated rotation + session recording + discovery; business from $6/user/mo.
**Pattern:** every vendor rotates **what has an API** (databases, cloud IAM, AD) and assumes
secrets are **already centralized in their store**. None of them owns *discovery of sprawl on a
local machine* or *the no-API long tail*.
---
## 2. PAM market — size & leaders
- **Size 2025→2026:** Mordor **$4.25B → $5.17B**; Fortune Business Insights **$4.51B → $5.58B**.
- **Growth:** Mordor → **$13.83B by 2031 (~21.7% CAGR)**; Precedence → up to ~$30B by 2034 (~23.8%).
- **Leaders:** CyberArk, BeyondTrust, Delinea, One Identity, ARCON, Wallix, ManageEngine, Okta.
- **Why rotation matters here:** automated rotation/vaulting is a **core PAM primitive**, now
driven by **machine identities outnumbering humans ~40:1**.
**Implication for incredigo:** do not compete in PAM. Be the tool that serves the people PAM
is too heavy/expensive/cloud-bound to serve.
---
## 3. Consumer auto-password-change — a graveyard
- **Dashlane** removed its automatic Password Changer in **early 2022** — officially "an
extremely complex feature that was prone to breaking." (Also killed its free plan Sept 16 2025.)
This is the canonical cautionary tale.
- **Chrome / Google Password Manager** — announced **Automated Password Change** at I/O 2025;
navigates the site and fills the change form **with per-change user approval** (no silent
action). Rollout late 2025, **limited English-language cooperative sites only**.
- **Apple** — no general web auto-changer; leans into **passkeys** + a change-password URL quirk.
- **Academic verdict (arxiv 2511.10111, Nov 2025, 111 Tranco sites):** *"no popular third-party
or browser-embedded password manager supports automated website password updates."* Sites use
**diverse, confusing** flows, often **break autocomplete**, and some **deliberately block
automation**. The industry's real answer is **passkeys**, not auto-change.
**Implication:** fully-automatic browser rotation is a known tar pit. incredigo's **guided,
human-in-the-loop worklist** (rotate what's scriptable; hand the human a ranked, linked
checklist for the rest; never bypass MFA/CAPTCHA) is the *pragmatic* shape that survives where
"automate everything" died. This matches Hard Rule #5 (self-owned only) and #6 (dry-run default).
---
## 4. RFC 8615 `.well-known/change-password`
- W3C "A Well-Known URL for Changing Passwords" lets tools deep-link to a site's change page.
- **Adoption (Tranco top 200, 2025):** ~**12.2%** support the W3C path; ~**33.8%** support
Apple's competing URL-quirk. Adopters incl. Google, GitHub, WordPress, Facebook, X. **Most
redirects land on a generic security page, not the change form.**
- Consumed by Chrome and Safari; Bitwarden has open community requests.
**Implication:** good enough to power incredigo's **offline change-URL generation** (already
built: curated table + RFC 8615 well-known + host fallback), but thin enough that it can't be
the whole answer — reinforcing the rotate-vs-guide split.
---
## 5. Underserved segments (the white space)
**A. Solo devs & small teams with local secret sprawl.**
- **GitGuardian State of Secrets Sprawl 2026:** **29M new hardcoded secrets in 2025, +34% YoY**;
each live secret appeared in **~8 locations on the same machine** (.env, shell history, IDE
configs, cached tokens, build artifacts); **59% of compromised machines were CI/CD runners**.
- **Snyk:** ~28M credentials leaked on GitHub in 2025.
- No major vendor owns **discover-then-rotate of sprawl already on the box** (.aws/.netrc/ssh/
docker/kube) — they start *after* migration into their hub. → **incredigo Mode B.**
**B. Privacy-conscious / no-cloud.**
- HashiCorp's BSL relicense spawned **OpenBao** (LF/MPL-2.0 fork of Vault 1.14, joined OpenSSF
Jun 2025). Infisical (MIT) and Bitwarden Secrets Manager also serve no-cloud.
- **But all are servers/daemons.** A **local-first, RAM-only, single binary** with no
infrastructure is unoccupied ground. → **incredigo's core architecture.**
**C. The no-API "long tail."**
- *"Password managers won't rotate your bank login, email recovery keys, or IoT device admin
panels unless you manually initiate each one."* Enterprise PAM rotates what has an API; the
manual long tail is **explicitly out of scope**; the one consumer attempt (Dashlane) failed.
**incredigo's guided worklist.**
**D. OSS rotation specifically (not storage).** Even among OSS tools rotation is the weak spot
(Infisical retry/failure gaps; GCP "write your own function"; AWS native only first-party DBs).
---
## 6. Who incredigo should be tailored for
**Primary persona — "the security-literate solo dev / small-team lead."**
- Has secrets scattered across `.env`, `~/.aws`, `~/.netrc`, `~/.ssh`, `docker-compose.yml`,
`k8s/*.yaml`, plus a gopass store.
- Distrusts (or can't afford / can't legally use) a SaaS secrets hub. Wants self-custody.
- Comfortable on the CLI; wants **headless automation in CI** *and* an interactive TUI.
- Job-to-be-done: *"Find every credential I have, back them up safely, rotate the ones a script
can rotate, and give me a clean checklist for the rest — without anything ever leaving my
machine."*
**Secondary personas:**
- **Privacy/sovereignty buyers** (journalists, activists, infosec consultants, regulated EU
shops who can't ship secrets to US clouds) — value RAM-only / no-cloud + the PGP-as-root-of-trust model.
- **Homelab / self-hosted operators** — rotate Postgres/Redis/SSH/router/NAS creds with one tool.
- **Incident responders / pentest cleanup** — after a leak, *discover + mass-rotate + audit* on a
host quickly. (Aligns with the GitGuardian "8 copies per secret" reality.)
**Explicit non-targets (for now):** Fortune-500 PAM, machine-identity-at-scale, compliance-cert
checkboxes (SOC2/FedRAMP managed service). Those are the incumbents' moats.
**Positioning line:** *"The local-first credential rotator: discover the secrets sprawled across
your machine, back them up, rotate what's automatable, and get a guided worklist for the rest —
RAM-only, no cloud, no server, one binary."*
---
## 7. Extension vectors (ranked by leverage vs. white space)
Tied to the gaps above, not feature-for-feature parity with PAM.
1. **Breadth of self-hostable rotation drivers** (the credibility multiplier). After postgres:
**MySQL/MariaDB, Redis, SSH `authorized_keys`, Gitea/GitHub/GitLab PATs, AWS IAM access keys
(moto-tested), HTTP basic-auth / reverse-proxy htpasswd, S3-compatible keys.** Each is a
self-contained `Rotator` — exactly the homelab/solo-dev surface incumbents skip.
2. **Consumer-grade reset-flow engine** (the highest-leverage *category* move): a generalized,
declarative **"email/OTP reset-flow" runner** for the long tail — site recipe = change-URL +
form selectors + (human-supplied) OTP step. Human-in-the-loop, never bypasses MFA. This is the
space Dashlane vacated and Chrome only half-fills.
3. **"Eliminate, don't rotate" recommender.** Where a credential *can* be replaced by something
that needs no rotation (passkeys, OIDC/SSO, short-lived certs, SSH certificates, cloud
workload identity), **recommend the elimination** instead of rotating. This rides the
industry's actual direction (passkeys) and is a differentiator no rotator markets.
4. **Consumer propagation / blast-radius closeout.** After rotating a secret, update the places
that consume it (.env, CI variables, k8s secrets) — the read-only `blast` map already finds
them; the next step is *guided* (not silent) propagation.
5. **GitOps / IaC rotation** — rotate a secret and open a PR that updates the encrypted value
(SOPS/age) in the repo. Serves the "59% of leaks are in CI/CD runners" reality.
6. **Standards plumbing** — RFC 7009 token revocation, RFC 8693 token exchange, SCIM for
user-store rotation, ACME for TLS cert rotation. Each widens automatable coverage.
**Recommended next two:** finish the **driver ladder** (#1 — proves the core works broadly and is
low-risk) and prototype the **reset-flow engine** (#2 — the actual unoccupied market). #3
(eliminate-don't-rotate) is the cheapest *positioning* win and worth a small advisory module.
---
## Caveats on data quality
- **PAM market sizes differ materially by analyst** (Mordor vs Fortune vs Precedence) — cite the
firm with each figure.
- The **`.well-known` adoption (12.2% / 33.8%)** and **111-site automation study** come from
**arxiv 2511.10111**; some per-metric numbers in the HTML preprint were redacted — treat the
fine-grained automation percentages as **directional** and verify against the final PDF before
quoting precisely.
- **Doppler's funding/valuation** was not found in public trackers; needs a dedicated dig if required.
- Acquisition values/dates (CyberArk/PANW, HashiCorp/IBM) are as announced in 2025; confirm close
status before citing as completed.
## Sources
- IBM/HashiCorp: https://www.hashicorp.com/en/blog/hashicorp-officially-joins-the-ibm-family · https://siliconangle.com/2025/02/27/ibm-completes-6-4b-hashicorp-acquisition-following-regulatory-approvals/
- Vault pricing/license: https://infisical.com/blog/hashicorp-vault-pricing · https://envmanager.com/blog/hashicorp-vault-pricing
- CyberArk/PANW: https://www.paloaltonetworks.com/company/press/2025/palo-alto-networks-announces-agreement-to-acquire-cyberark--the-identity-security-leader · Conjur pricing: https://infisical.com/blog/cyberark-conjur-pricing
- Akeyless: https://www.akeyless.io/secrets-management/ · https://www.prnewswire.com/news-releases/akeyless-raises-65m-to-provide-secrets-management-as-a-service-for-devops-and-multi-cloud-environments-301679894.html
- Doppler/Infisical: https://www.doppler.com/blog/infisical-doppler-secrets-management-comparison-2025 · https://securityboulevard.com/2025/06/infisical-vs-doppler-secrets-management-comparison-for-2025/
- AWS/Azure/GCP: https://www.pulumi.com/blog/secrets-management-tools-guide/ · https://www.techleague.io/blog/security/aws-secrets-manager-vs-azure-key-vault-vs-gcp-secret-manager-2026/ · https://dev.to/kazuya_dev/aws-reinvent-2025-zero-touch-secret-rotation-now-available-for-your-third-party-secrets-sec230-fne
- PAM market: https://www.mordorintelligence.com/industry-reports/privileged-access-management-pam-market · https://www.fortunebusinessinsights.com/privileged-access-management-market-112360 · https://www.precedenceresearch.com/privileged-access-management-market
- PAM leaders: https://techcloudpro.com/blog/cyberark-vs-delinea-vs-beyondtrust-pam-comparison/ · https://www.beyondtrust.com/resources/competitor-comparisons/delinea-alternative
- Consumer auto-change: https://www.cloudwards.net/dashlane-review/ · https://thehackernews.com/2025/05/google-chrome-can-now-auto-change.html · https://developer.chrome.com/docs/identity/automated-password-change
- Academic study: https://arxiv.org/abs/2511.10111 (HTML: https://arxiv.org/html/2511.10111v2)
- RFC 8615 / change-password URL: https://www.rfc-editor.org/rfc/rfc8615.html · https://www.w3.org/TR/change-password-url/ · https://web.dev/articles/change-password-url
- Secrets sprawl: https://thehackernews.com/2026/03/the-state-of-secrets-sprawl-2026-9.html · https://snyk.io/articles/state-of-secrets/ · https://www.doppler.com/blog/what-is-secrets-sprawl-and-how-to-prevent-it-in-2025
- OSS/no-cloud: https://openssf.org/blog/2025/06/17/openbao-joins-the-openssf-to-advance-secure-secrets-management-in-open-source/ · https://infisical.com/blog/open-source-secrets-management-devops
- Long tail: https://lifetips.alibaba.com/tech-efficiency/automated-password-rotation-for-personal-accounts
-91
View File
@@ -1,91 +0,0 @@
<!doctype html><html><head><meta charset="utf-8"><style>
html,body{margin:0;background:#060504;font-family:'JetBrainsMono Nerd Font','JetBrains Mono',monospace}
.grid{display:flex;gap:0}
.card{width:300px;height:360px;display:flex;flex-direction:column;align-items:center;
justify-content:center;gap:24px;border-right:1px solid #1c1714}
.card:last-child{border:none}
.lbl{color:#F7B968;font-size:15px;font-weight:700;letter-spacing:1px}
.sub{color:#8a7c72;font-size:11px;margin-top:-16px}
/* shared font for the in-svg wordmarks */
.mono{font-family:'JetBrainsMono Nerd Font','JetBrainsMonoNL Nerd Font','JetBrains Mono','Fira Code',monospace;font-weight:700}
</style></head><body>
<div class="grid">
<!-- ===== A: dark tile, igo, diamond i-dot (hero) ===== -->
<div class="card">
<svg width="180" height="180" viewBox="0 0 200 200">
<defs>
<linearGradient id="ta" x1="0" y1="0" x2="200" y2="200" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#1A1410"/><stop offset="1" stop-color="#0B0806"/></linearGradient>
<linearGradient id="la" x1="40" y1="80" x2="160" y2="150" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#F7B968"/><stop offset="0.6" stop-color="#E8772E"/><stop offset="1" stop-color="#C2531A"/></linearGradient>
</defs>
<rect width="200" height="200" rx="44" fill="url(#ta)" stroke="#2a201a" stroke-width="2"/>
<!-- custom i stem -->
<rect x="44" y="92" width="15" height="54" rx="6" fill="url(#la)"/>
<!-- diamond i-dot (nested strata) -->
<path d="M51.5 60 L66 74 L51.5 88 L37 74 Z" fill="none" stroke="url(#la)" stroke-width="4"/>
<path d="M51.5 68 L58 74 L51.5 80 L45 74 Z" fill="#F7B968"/>
<!-- g o -->
<text x="128" y="146" text-anchor="middle" class="mono" font-size="96" fill="url(#la)">go</text>
</svg>
<div class="lbl">A · DIAMOND-DOT</div><div class="sub">dark tile · i-dot = Strata core</div>
</div>
<!-- ===== B: orange tile, knockout igo ===== -->
<div class="card">
<svg width="180" height="180" viewBox="0 0 200 200">
<defs>
<linearGradient id="tb" x1="0" y1="0" x2="200" y2="200" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#F8A23E"/><stop offset="1" stop-color="#C2531A"/></linearGradient>
</defs>
<rect width="200" height="200" rx="44" fill="url(#tb)"/>
<rect x="44" y="92" width="15" height="54" rx="6" fill="#1A0E06"/>
<path d="M51.5 60 L66 74 L51.5 88 L37 74 Z" fill="#1A0E06"/>
<text x="128" y="146" text-anchor="middle" class="mono" font-size="96" fill="#1A0E06">go</text>
</svg>
<div class="lbl">B · KNOCKOUT</div><div class="sub">orange tile · bold solid</div>
</div>
<!-- ===== C: Strata mark + igo lockup ===== -->
<div class="card">
<svg width="180" height="180" viewBox="0 0 200 200">
<defs>
<linearGradient id="tc" x1="0" y1="0" x2="200" y2="200" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#1A1410"/><stop offset="1" stop-color="#0B0806"/></linearGradient>
<linearGradient id="lc" x1="40" y1="20" x2="160" y2="170" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#F7B968"/><stop offset="0.6" stop-color="#E8772E"/><stop offset="1" stop-color="#C2531A"/></linearGradient>
</defs>
<rect width="200" height="200" rx="44" fill="url(#tc)" stroke="#2a201a" stroke-width="2"/>
<g fill="none" stroke="url(#lc)" stroke-linejoin="round" transform="translate(100,78)">
<path d="M0 -46 L46 0 L0 46 L-46 0 Z" stroke-width="8" opacity="0.45"/>
<path d="M0 -26 L26 0 L0 26 L-26 0 Z" stroke-width="8"/>
</g>
<path d="M100 70 L108 78 L100 86 L92 78 Z" fill="#F7B968" transform="translate(0,0)"/>
<text x="100" y="168" text-anchor="middle" class="mono" font-size="46" letter-spacing="4" fill="url(#lc)">igo</text>
</svg>
<div class="lbl">C · STACK</div><div class="sub">Strata mark over igo</div>
</div>
<!-- ===== D: ig + diamond-o (o = vault) ===== -->
<div class="card">
<svg width="180" height="180" viewBox="0 0 200 200">
<defs>
<linearGradient id="td" x1="0" y1="0" x2="200" y2="200" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#1A1410"/><stop offset="1" stop-color="#0B0806"/></linearGradient>
<linearGradient id="ld" x1="30" y1="80" x2="170" y2="150" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#F7B968"/><stop offset="0.6" stop-color="#E8772E"/><stop offset="1" stop-color="#C2531A"/></linearGradient>
</defs>
<rect width="200" height="200" rx="44" fill="url(#td)" stroke="#2a201a" stroke-width="2"/>
<text x="62" y="146" text-anchor="middle" class="mono" font-size="96" fill="url(#ld)">ig</text>
<!-- diamond-o -->
<g fill="none" stroke="url(#ld)" stroke-linejoin="round" transform="translate(146,116)">
<path d="M0 -30 L30 0 L0 30 L-30 0 Z" stroke-width="9"/>
<path d="M0 -13 L13 0 L0 13 L-13 0 Z" stroke-width="7"/>
</g>
</svg>
<div class="lbl">D · DIAMOND-O</div><div class="sub">the 'o' becomes the vault</div>
</div>
</div>
</body></html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 KiB

+23
View File
@@ -0,0 +1,23 @@
<svg width="1200" height="360" viewBox="0 0 1200 360" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Incredigo (igo) — guards your credentials in locked memory, leaves no trace on disk">
<defs>
<linearGradient id="Abg" x1="0" y1="0" x2="1200" y2="360" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#161210"/><stop offset="1" stop-color="#0C0907"/></linearGradient>
<linearGradient id="Aword" x1="360" y1="120" x2="360" y2="200" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#F7B968"/><stop offset="1" stop-color="#E8772E"/></linearGradient>
<linearGradient id="Atile" x1="0" y1="0" x2="200" y2="200" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1A1410"/><stop offset="1" stop-color="#0B0806"/></linearGradient><linearGradient id="Aink" x1="30" y1="80" x2="170" y2="150" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#F7B968"/><stop offset="0.6" stop-color="#E8772E"/><stop offset="1" stop-color="#C2531A"/></linearGradient>
</defs>
<rect width="1200" height="360" rx="20" fill="url(#Abg)"/>
<g transform="translate(56,70) scale(1.1)">
<rect width="200" height="200" rx="44" fill="url(#Atile)" stroke="#2a201a" stroke-width="2"/>
<rect x="36" y="98" width="12" height="48" rx="6" fill="url(#Aink)"/>
<path d="M42 69 L51 78 L42 87 L33 78 Z" fill="#F7B968"/>
<text x="104" y="146" text-anchor="middle" font-family="'JetBrainsMono Nerd Font','JetBrains Mono','Fira Code',monospace" font-weight="700" font-size="96" fill="url(#Aink)">go</text>
</g>
<text x="360" y="158" font-family="'JetBrainsMono Nerd Font','JetBrains Mono','Fira Code',monospace" font-size="68" font-weight="700" letter-spacing="3" fill="#8C5E2E"><tspan>in</tspan><tspan fill="url(#Aword)">CRED</tspan><tspan>i</tspan><tspan fill="url(#Aword)">GO</tspan></text>
<text x="362" y="206" font-family="'Inter','DejaVu Sans',sans-serif" font-size="25" font-weight="500" fill="#C9BBB0">Guards your credentials in locked memory.</text>
<text x="362" y="240" font-family="'Inter','DejaVu Sans',sans-serif" font-size="25" font-weight="500" fill="#C9BBB0">Leaves no trace on disk.</text>
<g font-family="'JetBrainsMono Nerd Font','JetBrains Mono',monospace" font-size="16" font-weight="600">
<g transform="translate(362,278)"><rect width="150" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="75" y="22" text-anchor="middle" fill="#F7B968">local-first</text></g>
<g transform="translate(524,278)"><rect width="138" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="69" y="22" text-anchor="middle" fill="#F7B968">RAM-only</text></g>
<g transform="translate(674,278)"><rect width="118" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="59" y="22" text-anchor="middle" fill="#F7B968">offline</text></g>
<g transform="translate(804,278)"><rect width="196" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="98" y="22" text-anchor="middle" fill="#F7B968">no plaintext at rest</text></g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB

+23
View File
@@ -0,0 +1,23 @@
<svg width="1200" height="360" viewBox="0 0 1200 360" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Incredigo (igo) — guards your credentials in locked memory, leaves no trace on disk">
<defs>
<linearGradient id="Bbg" x1="0" y1="0" x2="1200" y2="360" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#161210"/><stop offset="1" stop-color="#0C0907"/></linearGradient>
<linearGradient id="Bword" x1="360" y1="120" x2="360" y2="200" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#F7B968"/><stop offset="1" stop-color="#E8772E"/></linearGradient>
<linearGradient id="Btile" x1="0" y1="0" x2="200" y2="200" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#F8A23E"/><stop offset="1" stop-color="#C2531A"/></linearGradient>
</defs>
<rect width="1200" height="360" rx="20" fill="url(#Bbg)"/>
<g transform="translate(56,70) scale(1.1)">
<rect width="200" height="200" rx="44" fill="url(#Btile)"/>
<rect x="36" y="98" width="12" height="48" rx="6" fill="#1A0E06"/>
<path d="M42 69 L51 78 L42 87 L33 78 Z" fill="#1A0E06"/>
<text x="104" y="146" text-anchor="middle" font-family="'JetBrainsMono Nerd Font','JetBrains Mono','Fira Code',monospace" font-weight="700" font-size="96" fill="#1A0E06">go</text>
</g>
<text x="360" y="158" font-family="'JetBrainsMono Nerd Font','JetBrains Mono','Fira Code',monospace" font-size="68" font-weight="700" letter-spacing="3" fill="#8C5E2E"><tspan>in</tspan><tspan fill="url(#Bword)">CRED</tspan><tspan>i</tspan><tspan fill="url(#Bword)">GO</tspan></text>
<text x="362" y="206" font-family="'Inter','DejaVu Sans',sans-serif" font-size="25" font-weight="500" fill="#C9BBB0">Guards your credentials in locked memory.</text>
<text x="362" y="240" font-family="'Inter','DejaVu Sans',sans-serif" font-size="25" font-weight="500" fill="#C9BBB0">Leaves no trace on disk.</text>
<g font-family="'JetBrainsMono Nerd Font','JetBrains Mono',monospace" font-size="16" font-weight="600">
<g transform="translate(362,278)"><rect width="150" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="75" y="22" text-anchor="middle" fill="#F7B968">local-first</text></g>
<g transform="translate(524,278)"><rect width="138" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="69" y="22" text-anchor="middle" fill="#F7B968">RAM-only</text></g>
<g transform="translate(674,278)"><rect width="118" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="59" y="22" text-anchor="middle" fill="#F7B968">offline</text></g>
<g transform="translate(804,278)"><rect width="196" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="98" y="22" text-anchor="middle" fill="#F7B968">no plaintext at rest</text></g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

+23
View File
@@ -0,0 +1,23 @@
<svg width="1200" height="360" viewBox="0 0 1200 360" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Incredigo (igo) — guards your credentials in locked memory, leaves no trace on disk">
<defs>
<linearGradient id="Cbg" x1="0" y1="0" x2="1200" y2="360" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#161210"/><stop offset="1" stop-color="#0C0907"/></linearGradient>
<linearGradient id="Cword" x1="360" y1="120" x2="360" y2="200" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#F7B968"/><stop offset="1" stop-color="#E8772E"/></linearGradient>
<linearGradient id="Ctile" x1="0" y1="0" x2="200" y2="200" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1A1410"/><stop offset="1" stop-color="#0B0806"/></linearGradient><linearGradient id="Cink" x1="40" y1="20" x2="160" y2="170" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#F7B968"/><stop offset="0.6" stop-color="#E8772E"/><stop offset="1" stop-color="#C2531A"/></linearGradient><radialGradient id="Ccore" cx="100" cy="78" r="14" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#FBD9A6"/><stop offset="1" stop-color="#F08A2C"/></radialGradient>
</defs>
<rect width="1200" height="360" rx="20" fill="url(#Cbg)"/>
<g transform="translate(56,70) scale(1.1)">
<rect width="200" height="200" rx="44" fill="url(#Ctile)" stroke="#2a201a" stroke-width="2"/>
<g fill="none" stroke="url(#Cink)" stroke-linejoin="round" transform="translate(100,78)"><path d="M0 -46 L46 0 L0 46 L-46 0 Z" stroke-width="7" opacity="0.42"/><path d="M0 -29 L29 0 L0 29 L-29 0 Z" stroke-width="7"/></g>
<path d="M100 66 L112 78 L100 90 L88 78 Z" fill="url(#Ccore)"/>
<text x="100" y="172" text-anchor="middle" font-family="'JetBrainsMono Nerd Font','JetBrains Mono','Fira Code',monospace" font-weight="700" font-size="42" letter-spacing="4" fill="url(#Cink)">igo</text>
</g>
<text x="360" y="158" font-family="'JetBrainsMono Nerd Font','JetBrains Mono','Fira Code',monospace" font-size="68" font-weight="700" letter-spacing="3" fill="#8C5E2E"><tspan>in</tspan><tspan fill="url(#Cword)">CRED</tspan><tspan>i</tspan><tspan fill="url(#Cword)">GO</tspan></text>
<text x="362" y="206" font-family="'Inter','DejaVu Sans',sans-serif" font-size="25" font-weight="500" fill="#C9BBB0">Guards your credentials in locked memory.</text>
<text x="362" y="240" font-family="'Inter','DejaVu Sans',sans-serif" font-size="25" font-weight="500" fill="#C9BBB0">Leaves no trace on disk.</text>
<g font-family="'JetBrainsMono Nerd Font','JetBrains Mono',monospace" font-size="16" font-weight="600">
<g transform="translate(362,278)"><rect width="150" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="75" y="22" text-anchor="middle" fill="#F7B968">local-first</text></g>
<g transform="translate(524,278)"><rect width="138" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="69" y="22" text-anchor="middle" fill="#F7B968">RAM-only</text></g>
<g transform="translate(674,278)"><rect width="118" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="59" y="22" text-anchor="middle" fill="#F7B968">offline</text></g>
<g transform="translate(804,278)"><rect width="196" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="98" y="22" text-anchor="middle" fill="#F7B968">no plaintext at rest</text></g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

+24
View File
@@ -0,0 +1,24 @@
<svg width="1200" height="360" viewBox="0 0 1200 360" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Incredigo (igo) — guards your credentials in locked memory, leaves no trace on disk">
<defs>
<linearGradient id="Dbg" x1="0" y1="0" x2="1200" y2="360" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#161210"/><stop offset="1" stop-color="#0C0907"/></linearGradient>
<linearGradient id="Dword" x1="360" y1="120" x2="360" y2="200" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#F7B968"/><stop offset="1" stop-color="#E8772E"/></linearGradient>
<linearGradient id="Dtile" x1="0" y1="0" x2="200" y2="200" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1A1410"/><stop offset="1" stop-color="#0B0806"/></linearGradient><linearGradient id="Dink" x1="20" y1="80" x2="180" y2="150" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#F7B968"/><stop offset="0.6" stop-color="#E8772E"/><stop offset="1" stop-color="#C2531A"/></linearGradient>
</defs>
<rect width="1200" height="360" rx="20" fill="url(#Dbg)"/>
<g transform="translate(56,70) scale(1.1)">
<rect width="200" height="200" rx="44" fill="url(#Dtile)" stroke="#2a201a" stroke-width="2"/>
<rect x="36" y="98" width="12" height="48" rx="6" fill="url(#Dink)"/>
<path d="M42 69 L51 78 L42 87 L33 78 Z" fill="#F7B968"/>
<text x="86" y="146" text-anchor="middle" font-family="'JetBrainsMono Nerd Font','JetBrains Mono','Fira Code',monospace" font-weight="700" font-size="96" fill="url(#Dink)">g</text>
<g fill="none" stroke="url(#Dink)" stroke-linejoin="round" transform="translate(146,116)"><path d="M0 -30 L30 0 L0 30 L-30 0 Z" stroke-width="9"/><path d="M0 -13 L13 0 L0 13 L-13 0 Z" stroke-width="7"/></g>
</g>
<text x="360" y="158" font-family="'JetBrainsMono Nerd Font','JetBrains Mono','Fira Code',monospace" font-size="68" font-weight="700" letter-spacing="3" fill="#8C5E2E"><tspan>in</tspan><tspan fill="url(#Dword)">CRED</tspan><tspan>i</tspan><tspan fill="url(#Dword)">GO</tspan></text>
<text x="362" y="206" font-family="'Inter','DejaVu Sans',sans-serif" font-size="25" font-weight="500" fill="#C9BBB0">Guards your credentials in locked memory.</text>
<text x="362" y="240" font-family="'Inter','DejaVu Sans',sans-serif" font-size="25" font-weight="500" fill="#C9BBB0">Leaves no trace on disk.</text>
<g font-family="'JetBrainsMono Nerd Font','JetBrains Mono',monospace" font-size="16" font-weight="600">
<g transform="translate(362,278)"><rect width="150" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="75" y="22" text-anchor="middle" fill="#F7B968">local-first</text></g>
<g transform="translate(524,278)"><rect width="138" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="69" y="22" text-anchor="middle" fill="#F7B968">RAM-only</text></g>
<g transform="translate(674,278)"><rect width="118" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="59" y="22" text-anchor="middle" fill="#F7B968">offline</text></g>
<g transform="translate(804,278)"><rect width="196" height="34" rx="17" fill="#1F1611" stroke="#D9661A" stroke-width="1.5"/><text x="98" y="22" text-anchor="middle" fill="#F7B968">no plaintext at rest</text></g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

+7
View File
@@ -0,0 +1,7 @@
<svg width="512" height="512" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="igo icon — diamond-dot on dark tile">
<defs><linearGradient id="Atile" x1="0" y1="0" x2="200" y2="200" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1A1410"/><stop offset="1" stop-color="#0B0806"/></linearGradient><linearGradient id="Aink" x1="30" y1="80" x2="170" y2="150" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#F7B968"/><stop offset="0.6" stop-color="#E8772E"/><stop offset="1" stop-color="#C2531A"/></linearGradient></defs>
<rect width="200" height="200" rx="44" fill="url(#Atile)" stroke="#2a201a" stroke-width="2"/>
<rect x="36" y="98" width="12" height="48" rx="6" fill="url(#Aink)"/>
<path d="M42 69 L51 78 L42 87 L33 78 Z" fill="#F7B968"/>
<text x="104" y="146" text-anchor="middle" font-family="'JetBrainsMono Nerd Font','JetBrains Mono','Fira Code',monospace" font-weight="700" font-size="96" fill="url(#Aink)">go</text>
</svg>

After

Width:  |  Height:  |  Size: 1015 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

+7
View File
@@ -0,0 +1,7 @@
<svg width="512" height="512" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="igo icon — knockout on orange tile">
<defs><linearGradient id="Btile" x1="0" y1="0" x2="200" y2="200" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#F8A23E"/><stop offset="1" stop-color="#C2531A"/></linearGradient></defs>
<rect width="200" height="200" rx="44" fill="url(#Btile)"/>
<rect x="36" y="98" width="12" height="48" rx="6" fill="#1A0E06"/>
<path d="M42 69 L51 78 L42 87 L33 78 Z" fill="#1A0E06"/>
<text x="104" y="146" text-anchor="middle" font-family="'JetBrainsMono Nerd Font','JetBrains Mono','Fira Code',monospace" font-weight="700" font-size="96" fill="#1A0E06">go</text>
</svg>

After

Width:  |  Height:  |  Size: 747 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

+7
View File
@@ -0,0 +1,7 @@
<svg width="512" height="512" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="igo icon — Strata mark over igo">
<defs><linearGradient id="Ctile" x1="0" y1="0" x2="200" y2="200" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1A1410"/><stop offset="1" stop-color="#0B0806"/></linearGradient><linearGradient id="Cink" x1="40" y1="20" x2="160" y2="170" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#F7B968"/><stop offset="0.6" stop-color="#E8772E"/><stop offset="1" stop-color="#C2531A"/></linearGradient><radialGradient id="Ccore" cx="100" cy="78" r="14" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#FBD9A6"/><stop offset="1" stop-color="#F08A2C"/></radialGradient></defs>
<rect width="200" height="200" rx="44" fill="url(#Ctile)" stroke="#2a201a" stroke-width="2"/>
<g fill="none" stroke="url(#Cink)" stroke-linejoin="round" transform="translate(100,78)"><path d="M0 -46 L46 0 L0 46 L-46 0 Z" stroke-width="7" opacity="0.42"/><path d="M0 -29 L29 0 L0 29 L-29 0 Z" stroke-width="7"/></g>
<path d="M100 66 L112 78 L100 90 L88 78 Z" fill="url(#Ccore)"/>
<text x="100" y="172" text-anchor="middle" font-family="'JetBrainsMono Nerd Font','JetBrains Mono','Fira Code',monospace" font-weight="700" font-size="42" letter-spacing="4" fill="url(#Cink)">igo</text>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

+8
View File
@@ -0,0 +1,8 @@
<svg width="512" height="512" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="igo icon — the o is a vault diamond">
<defs><linearGradient id="Dtile" x1="0" y1="0" x2="200" y2="200" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1A1410"/><stop offset="1" stop-color="#0B0806"/></linearGradient><linearGradient id="Dink" x1="20" y1="80" x2="180" y2="150" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#F7B968"/><stop offset="0.6" stop-color="#E8772E"/><stop offset="1" stop-color="#C2531A"/></linearGradient></defs>
<rect width="200" height="200" rx="44" fill="url(#Dtile)" stroke="#2a201a" stroke-width="2"/>
<rect x="36" y="98" width="12" height="48" rx="6" fill="url(#Dink)"/>
<path d="M42 69 L51 78 L42 87 L33 78 Z" fill="#F7B968"/>
<text x="86" y="146" text-anchor="middle" font-family="'JetBrainsMono Nerd Font','JetBrains Mono','Fira Code',monospace" font-weight="700" font-size="96" fill="url(#Dink)">g</text>
<g fill="none" stroke="url(#Dink)" stroke-linejoin="round" transform="translate(146,116)"><path d="M0 -30 L30 0 L0 30 L-30 0 Z" stroke-width="9"/><path d="M0 -13 L13 0 L0 13 L-13 0 Z" stroke-width="7"/></g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB