slash-port 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # slash-port
2
2
 
3
+ <!-- release:badge -->[![github](https://img.shields.io/badge/github-v0.3.0-0f8b7d?logo=github&logoColor=white)](https://github.com/Slash-ui/slash-port/releases/tag/v0.3.0)<!-- /release:badge -->
3
4
  [![npm](https://img.shields.io/npm/v/slash-port?logo=npm&logoColor=white&color=0f8b7d)](https://www.npmjs.com/package/slash-port)
4
5
  [![downloads](https://img.shields.io/npm/dm/slash-port?color=0f8b7d)](https://www.npmjs.com/package/slash-port)
5
6
  [![CI](https://github.com/Slash-ui/slash-port/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Slash-ui/slash-port/actions/workflows/ci.yml)
@@ -18,15 +19,60 @@ a good idea. `slash-port` answers all three, then kills the process for you
18
19
  after a confirmation that names it.
19
20
 
20
21
  ```
21
- slash-port 6/6 tcp
22
- PORT PID USER PROCESS DESCRIPTION
23
- 3000/tcp 41822 amin node Next.js (shop)
24
- 5173/tcp 41905 amin node Vite dev server (admin)
25
- 5432/tcp 612 postgres postgres PostgreSQL
26
- 6379/tcp 788 redis redis-server Redis
27
- 8080/tcp 39044 amin docker-proxy Docker published port
28
- 22/tcp 1 root sshd OpenSSH server [protected]
29
- ↑↓/jk move · PgUp/PgDn/g/G jump · / filter · x kill · r rescan · u udp · q quit
22
+ slash-port 8/8 tcp · beginner
23
+ PORT WHAT IT IS OPEN AT CLOSE IT?
24
+ 22/tcp OpenSSH server [protected] - No
25
+ 3000/tcp Next.js (shop) http://localhost:3000 Yes
26
+ 5173/tcp Vite dev server (admin) http://localhost:5173 Yes
27
+ 5432/tcp Docker Desktop - Probably
28
+ 6379/tcp Redis - Probably
29
+ 8025/tcp Docker Desktop http://localhost:8025 Probably
30
+ 49470/tcp Visual Studio Code (Node.js) - Probably
31
+ 51061/tcp macOS Handoff - Better not
32
+ ╭──────────────────────────────────────────────────────────────────────────────╮
33
+ │ Port 3000 · Next.js (shop) - a web server │
34
+ │ Point a browser at it - that is what it is there for. │
35
+ │ Project shop │
36
+ │ Open http://localhost:3000 │
37
+ │ Started by you (slashui) · node · pid 41822 │
38
+ │ Close it Yes - yours, and as easy to start again as it was to start │
39
+ │ Afterwards Start it again with your dev command, usually `npm run dev`. │
40
+ ╰──────────────────────────────────────────────────────────────────────────────╯
41
+ ↑↓ move · / find · x close it · r refresh · u udp · d details · m advanced · q …
42
+ ```
43
+
44
+ Every row says something the process name does not. The process behind
45
+ `Visual Studio Code` is called `Code Helper`, and the one behind `macOS
46
+ Handoff` is called `rapportd`. Neither name would have told you anything, which
47
+ is the point: when nothing at all can be worked out, the column says `-` rather
48
+ than repeating the process name back at you.
49
+
50
+ Press `m` for advanced mode, which trades the explanations for the facts that
51
+ tell two identical-looking dev servers apart:
52
+
53
+ ```
54
+ slash-port 8/8 tcp · advanced
55
+ PORT PID USER PROCESS ADDRESS DESCRIPTION
56
+ 22/tcp 947 root sshd * OpenSSH server [protected]
57
+ 3000/tcp 41822 slashui node * Next.js (shop)
58
+ 5173/tcp 41905 slashui node * Vite dev server (admin)
59
+ 5432/tcp 39044 slashui com.docker.backend * Docker Desktop
60
+ 6379/tcp 788 slashui redis-server 127.0.0.1 Redis
61
+ 8025/tcp 39044 slashui com.docker.backend * Docker Desktop
62
+ 49470/tcp 1737 slashui Code Helper 127.0.0.1 Visual Studio Code (Node.js)
63
+ 51061/tcp 694 slashui rapportd * macOS Handoff
64
+ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
65
+ │ Process node · pid 41822 · parent 1204 zsh │
66
+ │ User slashui │
67
+ │ Listening * · TCP · IPv4 │
68
+ │ Clients 3 connections open │
69
+ │ Running 2h 11m (since 14:02) │
70
+ │ Memory 431 MB · 0.4% CPU │
71
+ │ Directory /Users/slashui/code/shop │
72
+ │ Command node /Users/slashui/code/shop/node_modules/.bin/next dev │
73
+ │ Kill Yes - yours, and as easy to start again as it was to start. Start it again with your … │
74
+ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
75
+ ↑↓/jk move · PgUp/PgDn/g/G jump · / filter · x kill · r rescan · u udp · d detail · m beginner · q …
30
76
  ```
31
77
 
32
78
  ## Install
@@ -43,18 +89,122 @@ npx slash-port
43
89
 
44
90
  Requires Node 22 or newer. Works on Linux, macOS, and Windows.
45
91
 
92
+ ### Updating
93
+
94
+ The current release is
95
+ <!-- release:version -->0.3.0<!-- /release:version -->. To see what you have,
96
+ and what is published:
97
+
98
+ ```sh
99
+ slash-port --version # the one you are running
100
+ npm view slash-port version # the one on npm
101
+ ```
102
+
103
+ To move to the latest:
104
+
105
+ ```sh
106
+ npm install -g slash-port@latest
107
+ ```
108
+
109
+ `npm install -g …@latest` rather than `npm update -g slash-port`, because
110
+ `update` will not cross a major version - and before 1.0.0 it will not cross a
111
+ minor one either, which is every release this tool has had so far. Naming
112
+ `@latest` always gets you the newest published version.
113
+
114
+ `npx` caches the version it first downloaded, so ask it for the latest
115
+ explicitly:
116
+
117
+ ```sh
118
+ npx slash-port@latest
119
+ ```
120
+
121
+ There is no automatic update check. `slash-port` makes no network connections
122
+ at all, which means it will never tell you a new version exists - you find out
123
+ here, or from npm. Upgrading is safe: there is no state, no configuration file,
124
+ and nothing to migrate. To go the other way, name the version you want -
125
+ `npm install -g slash-port@0.1.0` - and to remove it entirely:
126
+
127
+ ```sh
128
+ npm uninstall -g slash-port
129
+ ```
130
+
131
+ Breaking changes are listed under **Breaking changes** in the
132
+ [changelog](CHANGELOG.md), so a major - or, before 1.0.0, a minor - is worth
133
+ reading before you take it.
134
+
46
135
  ## Use
47
136
 
48
137
  ```sh
49
- slash-port # the interactive list
138
+ slash-port # the interactive list, in beginner mode
139
+ slash-port --advanced # the same list, with the full detail
50
140
  slash-port 3000 # open on port 3000
51
141
  slash-port 3xxx # every port from 3000 to 3999
52
142
  slash-port 3000:3005 # every port in that range
53
143
  slash-port --plain # a plain table, for a pipe or a script
54
144
  slash-port --json # the same data as JSON
55
145
  slash-port --udp # include UDP as well as TCP
146
+ slash-port --docker # name the container behind a published port
56
147
  ```
57
148
 
149
+ ### Beginner and advanced
150
+
151
+ There are two modes, and **beginner is the default**. The person who does not
152
+ know what took port 3000 is the person who went looking for a tool that would
153
+ tell them; anyone who already knows can say `--advanced` once, or set
154
+ `SLASH_PORT_MODE=advanced` and never say it again. `m` switches between them
155
+ at any time, and `d` hides the panel in either.
156
+
157
+ | | Beginner | Advanced |
158
+ | --- | --- | --- |
159
+ | Columns | Port, what it is, where to open it, whether to close it | Port, pid, user, process, address, description |
160
+ | Narrow terminals | Keeps the verdict down to forty-two columns; the URL column stands down first, and stands down entirely when no row has one | Drops address, user, process, pid, in that order |
161
+ | Panel | What kind of thing it is, which project, who started it, whether closing it is a good idea, and how to start it again | Parent process, open connections, uptime, memory, working directory, full command line, and how the description was arrived at |
162
+ | Confirmation | Says what closing it costs and how to undo it | Names the signal |
163
+ | Cost | One scan | One scan, plus a lookup for the row under the cursor |
164
+
165
+ Advanced mode's extra facts are fetched for the selected row only. A machine
166
+ with four hundred listening sockets would otherwise pay four hundred times over
167
+ for facts you are reading one row at a time.
168
+
169
+ ### Docker
170
+
171
+ A published container port shows as `Docker Desktop`, which is true and no help
172
+ at all - and the beginner panel says so rather than leaving you to wonder where
173
+ the name went:
174
+
175
+ ```
176
+ ╭──────────────────────────────────────────────────────────────────────────────╮
177
+ │ Port 5432 · Docker Desktop - a container port │
178
+ │ Publishing a port on behalf of a container. │
179
+ │ Container not looked up - re-run with --docker to name it │
180
+ │ Started by you (slashui) · com.docker.backend · pid 39044 │
181
+ │ Close it Probably - yours, but something may be relying on it │
182
+ │ Afterwards Stopping the container that owns the port is the change you prob… │
183
+ ╰──────────────────────────────────────────────────────────────────────────────╯
184
+ ```
185
+
186
+ `--docker` asks the local engine which container is behind the port. The image
187
+ is read the same way a command line is, so `postgres:16` reports PostgreSQL and
188
+ is treated with the care a database deserves, and the compose project becomes
189
+ the hint - which is how two Supabase stacks on 5432 and 54322 stop being
190
+ interchangeable:
191
+
192
+ ```
193
+ ╭──────────────────────────────────────────────────────────────────────────────╮
194
+ │ Port 5432 · PostgreSQL in Docker (shop) - a database │
195
+ │ The container shop-db, from the shop compose project, running postgres:16-a… │
196
+ │ Container shop │
197
+ │ Started by you (slashui) · com.docker.backend · pid 39044 │
198
+ │ Close it Probably - stop the container instead: `docker stop shop-db` │
199
+ │ Afterwards Bring it back with `docker compose up -d db`. │
200
+ ╰──────────────────────────────────────────────────────────────────────────────╯
201
+ ```
202
+
203
+ It stays off until you ask for it. Reading two local tables and stopping is the
204
+ property this tool is built around, and a nicer default is not worth trading it
205
+ for. `SLASH_PORT_DOCKER=1` turns it on for good if you would rather not type
206
+ it, and `--no-docker` overrules that for one run.
207
+
58
208
  ### Naming ports
59
209
 
60
210
  `--port` and the interactive filter take the same three forms:
@@ -67,7 +217,7 @@ slash-port --udp # include UDP as well as TCP
67
217
 
68
218
  A pattern is read as digits, not as a number, so `3xxx` is the four-digit ports
69
219
  beginning with 3 and does not include 300. Ports above 65535 do not exist, so a
70
- pattern that can only match them — `7xxxx` — is a usage error rather than an
220
+ pattern that can only match them - `7xxxx` - is a usage error rather than an
71
221
  empty list.
72
222
 
73
223
  To kill something from a script, name it and confirm it:
@@ -96,6 +246,8 @@ There is no flag combination that kills something without naming it first.
96
246
  | `x` or `Enter` | Kill the selected port |
97
247
  | `r` | Rescan |
98
248
  | `u` | Show UDP as well as TCP |
249
+ | `m` | Switch between beginner and advanced |
250
+ | `d` | Show or hide the detail panel |
99
251
  | `q` | Quit |
100
252
 
101
253
  In the confirmation dialog: `y` sends SIGTERM, `f` forces with SIGKILL, `n`
@@ -106,7 +258,11 @@ cancels. The kill key is deliberately not next to a navigation key.
106
258
  | Option | Does |
107
259
  | --- | --- |
108
260
  | `-p`, `--port <ports>` | Only these ports: `3000`, `3xxx`, or `3000:3005` |
261
+ | `--beginner` | Explain each port in plain language. The default |
262
+ | `--advanced` | Show the full detail instead of the explanations |
263
+ | `--mode <name>` | `beginner` or `advanced`. `SLASH_PORT_MODE` sets the default |
109
264
  | `-u`, `--udp` | Include UDP sockets |
265
+ | `--docker` | Ask the local Docker socket which container holds a port. Off by default; `SLASH_PORT_DOCKER=1` sets the default |
110
266
  | `--json` | Print JSON and exit |
111
267
  | `--plain` | Print a plain table and exit |
112
268
  | `--kill` | Kill the process on `--port`. Requires `--yes` |
@@ -118,19 +274,43 @@ cancels. The kill key is deliberately not next to a navigation key.
118
274
  | `-h`, `--help` | Show help |
119
275
  | `-v`, `--version` | Show the version |
120
276
 
277
+ ### Output for scripts
278
+
279
+ `--json` is the stable machine surface. Fields are added over time and never
280
+ repurposed, so a `jq` expression written against an old version keeps working.
281
+ `description` is `null` rather than a copy of `process` when nothing could be
282
+ identified, which is the one thing worth knowing before parsing it: absence is
283
+ reported as absence.
284
+
285
+ `--plain` keeps the same six columns in both modes and always will, so anything
286
+ already splitting that output keeps working. A mode only ever appends a column
287
+ on the end - what closing it would mean in beginner mode, the full command line
288
+ in advanced.
289
+
121
290
  ### Exit codes
122
291
 
292
+ The same codes across every `slash-*` tool, so a script that wraps one can wrap
293
+ any of them:
294
+
123
295
  | Code | Means |
124
296
  | --- | --- |
125
297
  | `0` | Success |
126
- | `1` | The requested action could not be completed |
127
- | `2` | Invalid usage |
298
+ | `1` | Invalid arguments or usage |
299
+ | `2` | Nothing is listening on the ports asked about |
300
+ | `3` | Refused: a confirmation was missing, or a guardrail tripped |
301
+ | `4` | The operation was attempted and failed |
128
302
 
129
303
  Asking about a port is a question with a yes-or-no answer, so
130
- `slash-port --port 3000 --plain` exits `1` when nothing is listening there, and
304
+ `slash-port --port 3000 --plain` exits `2` when nothing is listening there, and
131
305
  so does `--port 3xxx` when nothing matches. Listing every port exits `0` even
132
306
  when the list is empty.
133
307
 
308
+ The distinction between `1` and `3` is whether the command made sense:
309
+ `--kill` with no `--port` did not name a target and exits `1`, while `--kill`
310
+ with a target but no `--yes` named one and did not confirm it, and exits `3`.
311
+ The standard reserves `5` for an integrity failure, which this tool has nothing
312
+ to verify and never returns.
313
+
134
314
  ## Safety
135
315
 
136
316
  Killing the wrong process at a terminal is easy and unrecoverable, so the rules
@@ -141,10 +321,21 @@ are fixed rather than configurable:
141
321
  - **A pattern or a range never kills on its own.** `--kill --port 3xxx --yes`
142
322
  is refused without `--all`, because what a pattern matches depends on what
143
323
  happens to be running when the command is run.
324
+ - **A signal that will bounce is flagged before you decide, not after.** A row
325
+ owned by somebody else is marked `[locked]`, and the confirmation says what
326
+ it would take to signal it - rather than letting you confirm a kill that was
327
+ never going to land.
144
328
  - **Some processes are refused outright**, before any dialog is offered: the
145
- init process, `sshd` — killing it locks you out of a remote machine — macOS
329
+ init process, `sshd` - killing it locks you out of a remote machine - macOS
146
330
  and Windows session processes, `slash-port` itself, and the shell that
147
331
  launched it.
332
+ - **Whether closing it is a good idea is settled before you decide.** Every row
333
+ carries a verdict - `Yes`, `Probably`, `Better not`, `No`, `Needs sudo` -
334
+ worked out from what it is, who owns it, and whether a guardrail already
335
+ refuses it. The verdict is a word in a column, never a colour alone.
336
+ - **The confirmation is never the thing that gets truncated.** On a terminal
337
+ too short to show it and the list behind it, the list gives way; a question
338
+ you cannot read is a question you cannot answer.
148
339
  - **SIGTERM before SIGKILL.** A process that ignores SIGTERM is reported as
149
340
  having survived. Escalating is a second, deliberate action, never automatic.
150
341
  - **A process that has already exited is never signalled**, because by then its
@@ -152,65 +343,113 @@ are fixed rather than configurable:
152
343
 
153
344
  On Windows there is no signal delivery: SIGTERM becomes `TerminateProcess`,
154
345
  which a process cannot catch or ignore, so nothing there gets the chance to
155
- shut down cleanly. The confirmation still applies — but "terminate" and "force"
346
+ shut down cleanly. The confirmation still applies - but "terminate" and "force"
156
347
  do the same thing.
157
348
 
158
349
  ## Privacy
159
350
 
160
- `slash-port` makes no network connections at any point. It reads the local
161
- socket table and the local process table, and that is all it does. There is no
162
- telemetry, no update check, and no configuration file.
351
+ `slash-port` makes no network connections at any point. By default it reads
352
+ the local socket table and the local process table, and asks nothing else on
353
+ the machine anything either. There is no telemetry, no update check, and no
354
+ configuration file.
355
+
356
+ `--docker` is the single exception, and it is off until you ask for it. It
357
+ reads the local Docker socket - a file on this machine, the same as
358
+ `/proc/net/tcp` is - to name the container behind a published port. A
359
+ `DOCKER_HOST` pointing at another machine over TCP is ignored rather than
360
+ connected to. `SLASH_PORT_DOCKER=1` turns it on for good if you would rather
361
+ not type it; `--no-docker` still overrules that for one run.
163
362
 
164
363
  ## Terminal behaviour
165
364
 
166
365
  - Only the sixteen named terminal colours, so the display inherits your theme
167
366
  rather than fighting it.
168
- - Colour never carries meaning on its own — a protected row is labelled
169
- `[protected]` as well as coloured.
367
+ - Colour never carries meaning on its own - a protected row is labelled
368
+ `[protected]` and one you cannot signal is labelled `[locked]`, as well as
369
+ being coloured.
170
370
  - `NO_COLOR` and `--no-color` are honoured.
171
371
  - Redirected or piped output is plain text with no control codes, and the
172
372
  interactive interface never starts unless both streams are a terminal.
173
373
  - The list is windowed to the visible rows, so a machine with four hundred
174
374
  listening sockets renders a screenful, not four hundred lines.
175
375
  - Columns are dropped in order of how little they carry as the window narrows,
176
- and values that are cut are marked with an ellipsis.
376
+ and values that are cut are marked with an ellipsis. Beginner mode keeps all
377
+ four of its columns down to eighty, because the fourth is the answer.
378
+ - A column that has nothing to show stands down rather than printing a column
379
+ of dashes: `OPEN AT` appears when the rows on screen have URLs.
177
380
 
178
381
  ## How it identifies a process
179
382
 
180
- Three sources, in priority order:
383
+ The description column exists to say something the process column did not. So
384
+ the first rule is that it never repeats the process name: when nothing could be
385
+ worked out, it says `-`, because "slash-port has no idea" is a fact and
386
+ `figma_agent` beside `figma_agent` is a column doing nothing.
387
+
388
+ What it consults, in priority order:
181
389
 
182
390
  1. **The command line.** Specific frameworks are matched before the runtimes
183
391
  that host them, so `node …/vite` reports Vite rather than Node.js.
184
- 2. **The project.** The directory above `node_modules` in the command line, so
185
- two Vite servers on 5173 and 5174 can be told apart.
186
- 3. **A well-known port registry**, used only when the process itself could not
187
- be identified — mostly other users' processes. Entries that would add
188
- nothing are suppressed: "dev server" on port 3000 is not information.
392
+ 2. **The container**, if you passed `--docker`. The local engine is asked which
393
+ container publishes the port, and the image is then read like a command
394
+ line, so `postgres:16` reports "PostgreSQL in Docker" and is treated with
395
+ the care a database deserves. The compose project - or the container name
396
+ when there is no project - becomes the hint, which is how `5432` and `54322`
397
+ stop being interchangeable. Without the flag a published port reports
398
+ `Docker Desktop`, and beginner mode says so rather than leaving you to
399
+ wonder why the name is missing.
400
+ 3. **The application.** The bundle or install directory the binary sits in,
401
+ so `Code Helper` reports Visual Studio Code and `figma_agent` reports Figma.
402
+ The outermost bundle wins, and a vendor directory beats the bundle inside it.
403
+ 4. **The project.** The directory above `node_modules` in the command line, so
404
+ two Vite servers on 5173 and 5174 can be told apart. A directory that only
405
+ names a convention - `lib`, `src`, `bin` - is passed over for the script
406
+ itself, which is why `…/google-cloud-sdk/lib/gcloud.py` reads as `gcloud`.
407
+ 5. **A well-known port registry**, for processes that could not be identified
408
+ at all - mostly other users'. Generic entries are demoted rather than
409
+ suppressed: "dev server" loses to anything specific, and beats saying
410
+ nothing.
411
+ 6. **The shape of the path.** A binary under `/usr/libexec` is a system service
412
+ whoever wrote it, which is worth more than its own name repeated back.
413
+
414
+ Beginner mode adds one more question on top of all that: whether closing it is
415
+ a good idea. That answer folds together what kind of thing it is, whether you
416
+ own it, and whether a guardrail already refuses it - and the last two win,
417
+ because they are facts about this machine rather than guesses about software. A
418
+ Postgres you do not own is `Needs sudo` whatever anyone thinks of closing
419
+ databases.
189
420
 
190
421
  Per platform:
191
422
 
192
423
  - **Linux** reads `/proc/net/tcp` and maps socket inodes through
193
424
  `/proc/[pid]/fd`. No `lsof`, which many container images do not have, and no
194
425
  subprocess. Descriptors belonging to other users are not readable without
195
- privileges, so those rows show no owner rather than failing the scan — run
426
+ privileges, so those rows show no owner rather than failing the scan - run
196
427
  with `sudo` to resolve them.
197
428
  - **macOS** uses `lsof` in field-output mode, plus `ps` for full command lines.
198
429
  - **Windows** uses `netstat -ano` and `tasklist`, which exist on every edition
199
430
  and avoid PowerShell's startup cost.
200
431
 
432
+ Advanced mode's per-row lookups follow the same rule of asking the cheapest
433
+ thing that can answer: `/proc` on Linux with no subprocess at all, `ps` and
434
+ `lsof` on macOS, and on Windows only what `tasklist` and `netstat` already
435
+ know. Parents, working directories, and start times need WMI or PowerShell
436
+ there, which cost about a second each, so those lines are left out rather than
437
+ paid for. A platform that cannot answer omits the line; it never guesses.
438
+
201
439
  ## Not built yet
202
440
 
203
441
  Deliberate omissions, listed so you know they are choices rather than
204
442
  oversights:
205
443
 
206
- - **Docker awareness.** A published port shows `docker-proxy` rather than the
207
- container behind it. Resolving that means talking to the Docker socket, which
208
- is a real dependency and belongs behind a flag.
444
+ - **Docker awareness by default.** `--docker` names the container behind a
445
+ published port, and it stays opt-in. Reading two local tables and stopping is
446
+ the property this tool is built around, and it is not worth trading for a
447
+ nicer default.
209
448
  - **Process trees.** Killing a dev server sometimes leaves children behind. A
210
449
  `--tree` option would signal the whole group.
211
450
  - **Watch mode.** The list rescans on `r`, not on a timer.
212
451
  - **Port history.** "What was on 3000 an hour ago" needs persistent state, and
213
- this tool currently has none — which is worth keeping.
452
+ this tool currently has none - which is worth keeping.
214
453
 
215
454
  ## Versioning and releases
216
455
 
@@ -222,6 +461,13 @@ and a patch. Every release is published with npm
222
461
  [provenance](https://docs.npmjs.com/generating-provenance-statements), so the
223
462
  tarball can be traced to the exact commit and workflow that built it.
224
463
 
464
+ One commit does the whole bump: `package.json`, the changelog entry, and the
465
+ version badge at the top of this file are written together, so the three cannot
466
+ name different versions. The badges either side of it are read live - npm and
467
+ the download count from the registry, CI and Release from the Actions API - so
468
+ the `github` badge and the `npm` badge agreeing means the publish landed, and
469
+ them disagreeing means it did not.
470
+
225
471
  See the [changelog](CHANGELOG.md) for what changed when.
226
472
 
227
473
  ## Contributing