yconn 1.5.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
# 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 init

# List all connections
yconn list

# Show full details for a connection
yconn 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 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 group list

# Show all connections (no group filter)
yconn list

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

# List only work connections
yconn list

# Connect to a work server
yconn connect work-web

# Switch to the private group
yconn group 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 group current

# Revert to no group filter (show all connections)
yconn group 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 group 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, using the input hostname directly.

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

```yaml
version: 1

connections:
  web-prod-*:
    host: ""          # ignored — the matched input becomes the SSH hostname
    user: deploy
    auth: key
    key: ~/.ssh/web_prod_key
    description: "Production web fleet (web-prod-01, web-prod-02, ...)"

  db-staging-?:
    host: ""          # ignored — the matched input becomes the SSH hostname
    user: dbadmin
    auth: key
    key: ~/.ssh/db_staging_key
    description: "Staging database servers (db-staging-a, db-staging-b, ...)"

  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; input becomes the SSH hostname
yconn connect web-prod-01

# Connect to web-prod-07 — same pattern, different host
yconn connect web-prod-07

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

# 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 show web-prod-01
```

**How wildcard 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 glob patterns against the input. `*` matches any
   sequence of characters; `?` matches any single character.
3. The matched input string (`web-prod-01`) becomes the SSH hostname directly. The
   `host:` field in the YAML entry is overridden by the matched input.
4. If two different patterns both match the same input (e.g. `web-*` and `web-prod-*`
   both match `web-prod-01`), yconn exits with a conflict error naming each pattern
   and its source file. Resolve this by making your patterns non-overlapping.

**Notes:**

- The `host:` field in wildcard entries is overridden at connect time. You can leave it
  blank or set it to a placeholder value — it will not be used for SSH.
- Wildcard entries appear in `yconn list` with their pattern name (e.g. `web-prod-*`) 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 init

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

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

**Resulting file trees:**

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

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

# yconn 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 init` fails with a clear error if the target file already exists.
- After running `yconn init`, edit the scaffolded file and run `yconn list` to verify.

---

## See also

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