yconn 1.11.0

SSH connection manager for teams and DevOps environments
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
# Examples

Copy-paste-ready scenarios for common yconn setups.

---

## Project-layer only

**When to use:** Your whole team works with the same set of servers and you want the connection
list checked into the project repository.

Create a project config file at the root of your repository:

```
your-project/
└── .yconn/
    └── connections.yaml
```

**`.yconn/connections.yaml`**

```yaml
version: 1

connections:
  prod-web:
    host: 10.0.1.50
    user: deploy
    port: 22
    auth: key
    key: ~/.ssh/prod_deploy_key
    description: "Primary production web server"
    link: https://wiki.internal/servers/prod-web

  staging-db:
    host: staging.internal
    user: dbadmin
    auth: password
    description: "Staging database server — use with caution"
    link: https://wiki.internal/servers/staging-db

  bastion:
    host: bastion.example.com
    user: ec2-user
    port: 2222
    auth: key
    key: ~/.ssh/bastion_key
    description: "Bastion host — jump point for internal network"
```

**Commands:**

```bash
# Scaffold the .yconn/ directory and connections.yaml in the current directory
yconn connections init

# List all connections
yconn list

# Show full details for a connection
yconn connections show prod-web

# Connect
yconn connect prod-web
yconn connect bastion
```

**Notes:**

- The `key` paths (e.g. `~/.ssh/prod_deploy_key`) are resolved on each developer's local
  machine. Every developer must have the referenced key files in place.
- Do not put passwords or key passphrases in this file — it lives in git. Key passphrases
  are handled by `ssh-agent`.
- Commit `.yconn/connections.yaml` to version control. All team members get the connection
  list automatically after `git pull`.

---

## Project + user layers

**When to use:** The team shares a project config, but you want to override one connection
locally — for example to use a different SSH key or a personal jump host.

**`.yconn/connections.yaml`** (committed to git — shared by the whole team)

```yaml
version: 1

connections:
  prod-web:
    host: 10.0.1.50
    user: deploy
    auth: key
    key: ~/.ssh/team_deploy_key
    description: "Primary production web server"

  staging-db:
    host: staging.internal
    user: dbadmin
    auth: password
    description: "Staging database server"
```

**`~/.config/yconn/connections.yaml`** (local to your machine — not in git)

```yaml
version: 1

connections:
  # Override the shared prod-web entry with your personal key
  prod-web:
    host: 10.0.1.50
    user: deploy
    auth: key
    key: ~/.ssh/my_personal_deploy_key
    description: "Primary production web server (personal key override)"

  # Add a private entry that only you need
  dev-vm:
    host: 192.168.1.5
    user: root
    auth: key
    key: ~/.ssh/id_rsa
    description: "Local development VM"
```

**Commands:**

```bash
# List connections — prod-web shows from user layer (your override wins)
yconn list

# See where prod-web comes from
yconn connections show prod-web
# Source: user (/home/you/.config/yconn/connections.yaml)

# See all entries including the shadowed team version
yconn list --all
# prod-web appears twice: user layer (active) and project layer (shadowed)

# Connect using your overridden key
yconn connect prod-web
```

**Notes:**

- The user layer (`~/.config/yconn/`) takes higher priority than the project layer
  (`.yconn/`). Any connection defined in both layers will use the user-layer values.
- The project layer entry for `prod-web` is still present — `yconn list --all` shows it
  with a `[shadowed]` tag.
- Unique connection names (like `dev-vm`) are simply merged in alongside project entries.

---

## Docker-enabled setup

**When to use:** You want SSH keys to live inside a Docker image rather than on developer
machines. Developers pull the image and connect without needing the key files locally.

**`.yconn/connections.yaml`** (committed to git)

```yaml
version: 1

docker:
  image: ghcr.io/myorg/yconn-keys:latest
  pull: missing   # pull if not already present locally
  args:
    - "--network=host"

connections:
  prod-web:
    host: 10.0.1.50
    user: deploy
    auth: key
    key: /keys/prod_deploy_key   # path inside the Docker image
    description: "Primary production web server"

  staging-db:
    host: staging.internal
    user: dbadmin
    auth: password
    description: "Staging database server"

  bastion:
    host: bastion.example.com
    user: ec2-user
    port: 2222
    auth: key
    key: /keys/bastion_key       # path inside the Docker image
    description: "Bastion host"
```

**Commands:**

```bash
# Connect — yconn detects the docker block, pulls the image if needed,
# and re-invokes itself inside the container
yconn connect prod-web

# See the full docker run command before execution
yconn connect --verbose prod-web

# Check Docker status and which image would be used
yconn config
```

**How it works:**

1. yconn starts on the host, loads the config, and finds `docker.image` set.
2. It checks whether it is already running inside a container (`/.dockerenv` or
   `CONN_IN_DOCKER=1`). It is not, so it builds a `docker run` command.
3. The container gets the yconn binary, all config layers, and the current working
   directory mounted. The original command (`connect prod-web`) is passed through.
4. Inside the container, yconn sees `CONN_IN_DOCKER=1` and skips the Docker step.
   It invokes SSH directly using the key at `/keys/prod_deploy_key` inside the image.

**Building the keys image** (example Dockerfile):

```dockerfile
FROM alpine:3.21
RUN apk add --no-cache openssh-client
COPY keys/prod_deploy_key /keys/prod_deploy_key
COPY keys/bastion_key /keys/bastion_key
RUN chmod 600 /keys/prod_deploy_key /keys/bastion_key
```

**Notes:**

- The `docker` block is only trusted from the project (`.yconn/`) and system
  (`/etc/yconn/`) layers. A `docker` block in `~/.config/yconn/` is ignored with a warning.
- Key paths in the connection entries (e.g. `/keys/prod_deploy_key`) are resolved
  inside the container, not on the host.
- Pass `--network=host` in `docker.args` only when the container needs to reach hosts
  on the host network directly.

---

## Inline group field usage

**When to use:** You have logically separate sets of connections — for example `work` and
`private` — and want to switch between them cleanly. All connections live in one
`connections.yaml` file; a `group:` field on each entry determines which set it belongs to.

**`~/.config/yconn/connections.yaml`**

```yaml
version: 1

connections:
  work-web:
    host: 10.10.0.5
    user: deploy
    auth: key
    key: ~/.ssh/work_key
    description: "Work production web server"
    group: work

  work-db:
    host: 10.10.0.10
    user: dbadmin
    auth: password
    description: "Work database server"
    group: work

  home-server:
    host: 192.168.1.100
    user: mans
    auth: key
    key: ~/.ssh/id_ed25519
    description: "Home server"
    group: private

  vps:
    host: vps.example.com
    user: root
    auth: key
    key: ~/.ssh/vps_key
    description: "Personal VPS"
    group: private
```

**Commands:**

```bash
# List all available group values found across connections
yconn groups list

# Show all connections (no group filter)
yconn list

# Switch to the work group — subsequent list/connect only shows work connections
yconn groups use work

# List only work connections
yconn list

# Connect to a work server
yconn connect work-web

# Switch to the private group
yconn groups use private

# List only private connections
yconn list

# Connect to the home server
yconn connect home-server

# Show connections from a specific group without changing the active group
yconn list --group work

# Show all connections regardless of active group
yconn list --all

# Check which group is currently active
yconn groups current

# Revert to no group filter (show all connections)
yconn groups clear
```

**Notes:**

- All connections live in `connections.yaml`. The `group:` field is just a tag — no
  separate files per group.
- The active group is stored in `~/.config/yconn/session.yml` and persists across
  terminal sessions until changed.
- Connections without a `group:` field are always shown when no group filter is active.
  When a group is locked, only tagged connections matching the group are shown.
- `yconn list --all` always overrides any group filter and shows every connection.
- `yconn groups use <name>` warns if no connections with that group value exist in any
  layer, but it still sets the group.

---

## Wildcard pattern usage

**When to use:** You manage many similarly-named hosts (e.g. a fleet of web servers) and
want a single connection entry to cover all of them.

**`.yconn/connections.yaml`**

```yaml
version: 1

connections:
  web-prod-*:
    host: "${name}.corp.com"   # ${name} expands to the matched input → web-prod-01.corp.com
    user: deploy
    auth: key
    key: ~/.ssh/web_prod_key
    description: "Production web fleet (web-prod-01, web-prod-02, ...)"

  db-staging-?:
    host: "${name}.db.internal"
    user: dbadmin
    auth: key
    key: ~/.ssh/db_staging_key
    description: "Staging database servers (db-staging-a, db-staging-b, ...)"

  "app[1..20]":
    host: "${name}.internal"   # app5 → app5.internal
    user: ops
    auth: key
    key: ~/.ssh/ops_key
    description: "App servers 1 through 20 (app1 … app20)"

  bastion:
    host: bastion.example.com
    user: ec2-user
    port: 2222
    auth: key
    key: ~/.ssh/bastion_key
    description: "Bastion host (exact match — takes priority over any pattern)"
```

**Commands:**

```bash
# Connect to web-prod-01 — matches web-prod-* pattern; SSH target is web-prod-01.corp.com
yconn connect web-prod-01

# Connect to web-prod-07 — same pattern, SSH target is web-prod-07.corp.com
yconn connect web-prod-07

# Connect to db-staging-a — matches db-staging-? pattern
yconn connect db-staging-a

# Connect to app5 — matches app[1..20] range; SSH target is app5.internal
yconn connect app5

# Connect to bastion — exact match wins over any wildcard pattern
yconn connect bastion

# Show which pattern covers a given input (shows pattern name in source field)
yconn connections show web-prod-01
```

**How pattern matching works:**

1. yconn first checks whether the input is an exact connection name. If found, it wins
   immediately — no pattern check is done.
2. All connection names are tested as patterns against the input. Two kinds are supported:
   - **Glob**`*` matches any sequence of characters; `?` matches any single character.
   - **Numeric range**`[N..M]` at the end of a name matches any input whose suffix after
     the literal prefix is an integer in `[N, M]` inclusive (e.g. `app[1..20]` matches
     `app1` through `app20`).
3. The `host:` field is resolved for the matched entry:
   - If `host` contains `${name}`, only that token is replaced with the matched input.
     `host: ${name}.corp.com` + input `web-prod-01` → SSH target `web-prod-01.corp.com`.
   - If `host` does not contain `${name}`, the entire field is replaced by the matched input
     (legacy behaviour — blank or placeholder hosts still work as before).
4. If two different patterns both match the same input (including a glob and a range),
   yconn exits with a conflict error naming each pattern and its source file. Resolve this
   by making your patterns non-overlapping.

**Notes:**

- Use `host: "${name}.corp.com"` to append a domain suffix to every matched input.
- Use `host: "${name}"` or leave host blank/placeholder for bare-hostname behaviour.
- Quote range-pattern YAML keys that contain `[`: `"app[1..20]"`.
- Pattern entries appear in `yconn list` with their raw pattern name (e.g. `app[1..20]`)
  in the NAME column.
- Same-pattern names across layers follow normal priority rules (higher layer wins) and
  do not trigger conflict detection.

---

## Multi-location init

**When to use:** You want to scaffold a `connections.yaml` at a specific location to match
your project conventions — the default `.yconn/` subdirectory, a dotfile, or a plain file.

The three `--location` values and their resulting paths:

| `--location` value | Resulting file path | Notes |
|---|---|---|
| `yconn` (default) | `.yconn/connections.yaml` | Isolated in subdirectory; git-trackable; recommended |
| `dotfile` | `.connections.yaml` | Hidden file in project root |
| `plain` | `connections.yaml` | Plain file in project root; may conflict with other tools |

**Commands:**

```bash
# Default — creates .yconn/connections.yaml
yconn connections init

# Dotfile convention — creates .connections.yaml in the current directory
yconn connections init --location dotfile

# Plain — creates connections.yaml in the current directory
yconn connections init --location plain
```

**Resulting file trees:**

```
# yconn connections init (default)
your-project/
└── .yconn/
    └── connections.yaml

# yconn connections init --location dotfile
your-project/
└── .connections.yaml

# yconn connections init --location plain
your-project/
└── connections.yaml
```

**Upward walk priority:**

When yconn searches upward from the working directory, it checks all three conventions
in each directory in this order:

1. `.yconn/connections.yaml`
2. `.connections.yaml`
3. `connections.yaml`

The first match in a given directory wins. The walk then moves up to the parent
directory and checks again.

**Notes:**

- All three conventions are recognised by the upward walk — you can mix conventions
  across different projects.
- `yconn connections init` fails with a clear error if the target file already exists.
- After running `yconn connections init`, edit the scaffolded file and run `yconn list` to verify.

---

## `users:` map and `${key}` expansion

**When to use:** You want to define a short alias for an SSH username that varies per developer or
per environment, and reference it in connection entries without repeating the actual value
everywhere.

**`~/.config/yconn/connections.yaml`** (user layer — private to your machine)

```yaml
version: 1

users:
  testuser: "testusername"   # your personal username on the prod cluster

connections:
  prod-web:
    host: 10.0.1.50
    user: ${testuser}           # expands to "testusername" at connect time
    auth: key
    key: ~/.ssh/prod_key
    description: "Production web server"

  staging:
    host: staging.internal
    user: ${user}             # expands to the $USER environment variable
    auth: password
    description: "Staging server (uses your local $USER)"
```

**Commands:**

```bash
# Connect — yconn expands ${testuser} to "testusername" before invoking SSH
yconn connect prod-web

# Override the users: entry for this invocation only (connect as alice instead)
yconn connect prod-web --user testuser:alice

# Override the ${user} env-var expansion for this invocation
yconn connect staging --user user:alice

# Inspect raw config values — yconn connections show does NOT expand templates
yconn connections show prod-web
# User: ${testuser}   ← raw value, not expanded

# List all user entries across all layers (with source and shadowing info)
yconn users show

# Add a new user entry interactively (defaults to user layer)
yconn users add

# Add to the project layer instead
yconn users add --layer project

# Add entries directly without the wizard (repeatable)
yconn users add --user testuser:alice --user deploy:bob
yconn users add --layer project --user testuser:carol

# Edit the source file for a named entry
yconn users edit testuser

# Generate SSH config — expands ${testuser} in User lines
yconn ssh-config

# Generate SSH config but skip all User lines
yconn ssh-config --skip-user

# Generate SSH config overriding testuser for this run
yconn ssh-config --user testuser:alice
```

**Notes:**

- `${key}` expansion looks up `key` in the merged `users:` map (project > user > system priority).
- `${user}` (lowercase, literal) is special — it expands from the `$USER` environment variable,
  not from a `users:` map entry. Named map lookup always happens first; `${user}` env-var
  expansion is a separate fallback step.
- If a `${key}` token cannot be resolved after all expansion steps, a warning is emitted to
  stderr and the literal template string is passed through unchanged to SSH.
- `yconn connections show` prints raw unexpanded field values — it never expands templates.
- `--user KEY:VALUE` overrides apply for one invocation only; they are not persisted to any
  config file.

---

## See also

- [Configuration reference]configuration.md — full field reference and layer system
- `man yconn` — full command reference