GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — feat: report port-forward sessions as app usage by nickvigilante · Pull Request #30156 · coder/coder

Conversation

@nickvigilante

@nickvigilante nickvigilante commented Sep 30, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

coder port-forward reported usage with the deprecated UpdateWorkspaceUsageContext.
It now reports a port_forwarding session, like coder ssh and the VS Code SSH path, but only while a forwarded TCP connection is open.

Behavior change: CLI output is unchanged.
Port-forward sessions previously only bumped the workspace's last-used time.
With the hidden workspace-usage experiment enabled, the server now also records port_forwarding session counts under a new port_forwarding family, which the deployment banner totals under Other.
Without the experiment, postWorkspaceUsage returns 204 before reading the body, so nothing changes.

Decisions
  • Added codersdk.UsageAppNamePortForwarding (port_forwarding) rather than reusing ssh, so port-forwarding isn't counted as interactive SSH. The name matches the arbitrary app names RFC's connection-method name.
  • Registered a port_forwarding family in codersdk/appname.go. Families resolve at read time, so no data migration is needed. Template insights list apps individually; telemetry, metricscache, and agentstats gauges are legacy fixed fields and stay as they are. The Prometheus insights family is left to feat!: label the VS Code family and report built-in app usage per app #29906.
  • Only TCP connections count. UDP forwards never see EOF, so one datagram would keep the session active until exit.
  • Reports once at startup, like the old call, then every minute and once on shutdown. An idle tick sends an empty body, which still bumps last-used.

Closes CLI-31

This PR was generated by Coder Agents on behalf of @nickvigilante.

CI note: unrelated test-go-race-pg flake

The test-go-race-pg job failed on a data race between tailnet.(*configMaps).updatePeerLocked and slog formatting of the same peer value, which does not touch port-forward code. The job was rerun.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@linear-code

linear-code Bot commented Sep 30, 2026

Copy link
Copy Markdown

CLI-31

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@github-actions github-actions Bot assigned nickvigilante Sep 30, 2026
@nickvigilante

nickvigilante commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

I think this test is flaky because of the issue that #30132 attempts to fix.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nickvigilante
nickvigilante marked this pull request as ready for review September 30, 2026 03:00
@nickvigilante
nickvigilante requested a review from matifali September 30, 2026 03:00
@matifali
matifali requested a review from EhabY September 30, 2026 04:36
@matifali

matifali commented Sep 30, 2026

Copy link
Copy Markdown
Member

@codex review

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 30, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-30T05:47:26.457737Z 41e81cd Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

chatgpt-codex-connector[bot]
chatgpt-codex-connector Bot reviewed Sep 30, 2026
View reviewed changes

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 41e81cdb20

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread codersdk/workspaces.go Resolved

Uh oh!

There was an error while loading. Please reload this page.

EhabY
EhabY reviewed Sep 30, 2026
View reviewed changes

@EhabY EhabY left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The server records these sessions only when the hidden workspace-usage experiment is on. Without it, postWorkspaceUsage returns 204 before it reads the body. Can you say that in the description?

The description also says the data feeds application usage insights and Prometheus insights metrics. While port_forward is unregistered, both drop it as the unknown family. Today it only reaches active users, usage minutes, the deployment banner, and telemetry.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread codersdk/workspaces.go Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread codersdk/workspaces.go Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread cli/portforward.go Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread cli/portforward_test.go Resolved

Uh oh!

There was an error while loading. Please reload this page.

@matifali Linear Code

matifali commented Sep 30, 2026

Copy link
Copy Markdown
Member

@f0ssel what do we want to do with workspace-usage experiment?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nickvigilante

nickvigilante commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

@EhabY Thanks, I updated the PR description.
It now says the server only records port_forwarding sessions when the workspace-usage experiment is on, since postWorkspaceUsage returns 204 before reading the body otherwise.
I also corrected the insights claim.
With the experiment on, the sessions reach active users, usage minutes, the deployment banner, and telemetry's raw per-app counts.
They don't reach per-template application usage insights or the Prometheus insights metrics, because both only emit the builtin families and drop port_forwarding as unknown.

Generated by Coder Agents on behalf of @nickvigilante.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

EhabY
EhabY reviewed Sep 30, 2026
View reviewed changes
Comment thread cli/portforward.go Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread codersdk/workspaces.go Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread cli/portforward.go Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread cli/portforward.go Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread cli/portforward.go Resolved

Uh oh!

There was an error while loading. Please reload this page.

@matifali Linear Code

matifali commented Oct 1, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

I would sugegst hold on to this until we are sure we are going to keep the experiment. I think there is plan to remove it PLAT-152

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nickvigilante

nickvigilante commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

I would sugegst hold on to this until we are sure we are going to keep the experiment. I think there is plan to remove it Deal-review workspace recovery, inputs, and upload guidance

Fair enough. I'm going to close this PR and park its corresponding issue until we settle on the plan you mentioned.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nickvigilante nickvigilante closed this Oct 1, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 1, 2026
@matifali Linear Code

matifali commented Oct 1, 2026

Copy link
Copy Markdown
Member

oops wrong issue linked. corrected

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@EhabY

EhabY commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Tbh I see no harm in merging this since it means we now correctly count coder port forwarding and is trackable, regardless of the experiment 🤷

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nickvigilante

nickvigilante commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

@matifali Let me know if we should reconsider. I'm happy to do what I'm told here.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@matifali Linear Code

matifali commented Oct 1, 2026

Copy link
Copy Markdown
Member

Yes we can. Its a net additive chnage so I am good adding it

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@matifali Linear Code

matifali commented Oct 1, 2026

Copy link
Copy Markdown
Member

Would leave review to @EhabY

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@matifali Linear Code

matifali commented Oct 1, 2026

Copy link
Copy Markdown
Member

One thing I would like to know is how this data would be consumed? Is it going to Template insights and what Prometheus metrics?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@EhabY

EhabY commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Currently it would only be shown in the Deployment banner, but after the scalable IDE work lands it'll be part of every other metric

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@EhabY EhabY reopened this Oct 1, 2026
@nickvigilante
nickvigilante force-pushed the vigilante/cli-31-cli-port-forward-calls-deprecated branch from 28cd398 to b205b39 Compare October 1, 2026 17:10
@coder coder unlocked this conversation Oct 2, 2026
EhabY
EhabY approved these changes Oct 2, 2026
View reviewed changes

@EhabY EhabY left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. A few optional simplifications inline.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread cli/portforward.go Outdated
Comment on lines +239 to +251
// request returns the next usage report. The body is empty, which only bumps
// the workspace's last-used time, unless a connection is open or opened since
// the previous call.
func (u *portForwardUsage) request(agentID uuid.UUID) codersdk.PostWorkspaceUsageRequest {
// Swap first so a connection that opened and closed between calls counts.
if u.seen.Swap(false) || u.active.Load() > 0 {
return codersdk.PostWorkspaceUsageRequest{
AgentID: agentID,
AppName: string(codersdk.UsageAppNamePortForwarding),
}
}
return codersdk.PostWorkspaceUsageRequest{}
}

@EhabY EhabY Oct 2, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tracker doesn't need to know the SDK request shape. Return a bool and let the reporter build the body (see below). TestPortForwardUsageRequest can then compare against u.inUse() with want []bool.

Suggested change
// request returns the next usage report. The body is empty, which only bumps
// the workspace's last-used time, unless a connection is open or opened since
// the previous call.
func (u *portForwardUsage) request(agentID uuid.UUID) codersdk.PostWorkspaceUsageRequest {
// Swap first so a connection that opened and closed between calls counts.
if u.seen.Swap(false) || u.active.Load() > 0 {
return codersdk.PostWorkspaceUsageRequest{
AgentID: agentID,
AppName: string(codersdk.UsageAppNamePortForwarding),
}
}
return codersdk.PostWorkspaceUsageRequest{}
}
// inUse reports whether a connection is open or opened since the last call.
func (u *portForwardUsage) inUse() bool {
// Swap first so a connection that opened and closed between calls counts.
return u.seen.Swap(false) || u.active.Load() > 0
}

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@nickvigilante nickvigilante Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in a55b934: the tracker now exposes inUse() bool and TestPortForwardUsageRequest compares against want []bool.

Generated by Coder Agents on behalf of @nickvigilante.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread cli/portforward.go
Comment on lines +256 to +260
report := func(ctx context.Context) {
if err := client.PostWorkspaceUsageWithBody(ctx, workspaceID, usage.request(agentID)); err != nil {
client.Logger().Warn(ctx, "failed to post workspace usage", slog.Error(err))
}
}

@EhabY EhabY Oct 2, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follows from the inUse() suggestion above.

Suggested change
report := func(ctx context.Context) {
if err := client.PostWorkspaceUsageWithBody(ctx, workspaceID, usage.request(agentID)); err != nil {
client.Logger().Warn(ctx, "failed to post workspace usage", slog.Error(err))
}
}
report := func(ctx context.Context) {
var req codersdk.PostWorkspaceUsageRequest
if usage.inUse() {
req = codersdk.PostWorkspaceUsageRequest{
AgentID: agentID,
AppName: string(codersdk.UsageAppNamePortForwarding),
}
}
if err := client.PostWorkspaceUsageWithBody(ctx, workspaceID, req); err != nil {
client.Logger().Warn(ctx, "failed to post workspace usage", slog.Error(err))
}
}

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@nickvigilante nickvigilante Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in a55b934: the reporter builds the PostWorkspaceUsageRequest itself from inUse().

Generated by Coder Agents on behalf of @nickvigilante.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread cli/portforward.go
Comment on lines +262 to +285
report(ctx)
ticker := time.NewTicker(time.Minute)
doneCh := make(chan struct{})
go func() {
defer func() {
ticker.Stop()
close(doneCh)
}()
for {
select {
case <-ticker.C:
report(ctx)
case <-ctx.Done():
// ctx is canceled, so send the final report on a detached one.
finalCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second)
report(finalCtx)
cancel()
return
}
}
}()
return func() {
<-doneCh
}

@EhabY EhabY Oct 2, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The returned func only waits, so it blocks forever unless the caller cancels ctx first (L193 happens to). Cancel internally like UpdateWorkspaceUsageWithBodyContext does, and use defer for cleanup. The doc on L254 would then read "Call the returned func to stop and wait for the final report."

Suggested change
report(ctx)
ticker := time.NewTicker(time.Minute)
doneCh := make(chan struct{})
go func() {
defer func() {
ticker.Stop()
close(doneCh)
}()
for {
select {
case <-ticker.C:
report(ctx)
case <-ctx.Done():
// ctx is canceled, so send the final report on a detached one.
finalCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second)
report(finalCtx)
cancel()
return
}
}
}()
return func() {
<-doneCh
}
ctx, cancel := context.WithCancel(ctx)
report(ctx)
done := make(chan struct{})
go func() {
defer close(done)
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
for {
select {
case <-ticker.C:
report(ctx)
case <-ctx.Done():
// ctx is canceled, so send the final report on a detached one.
finalCtx, finalCancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second)
report(finalCtx)
finalCancel()
return
}
}
}()
return func() {
cancel()
<-done
}

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@nickvigilante nickvigilante Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in a55b934: the returned func now cancels an internal context and waits for the final report, which is sent on a detached context with a 5s timeout. The doc comment is updated. The caller around L193 still calls its own cancel() for the command context, which is still needed to stop the listeners, so it is unchanged.

Generated by Coder Agents on behalf of @nickvigilante.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
nickvigilante added 2 commits October 2, 2026 13:53
@nickvigilante
feat: report port-forward sessions as app usage
60012cf
`coder port-forward` reported usage with the deprecated
`UpdateWorkspaceUsageContext`. It now uses
`UpdateWorkspaceUsageWithBodyContext` with the agent ID and a new
`port_forward` app name, like `coder ssh` and the VS Code SSH path.

Linear: CLI-31
@nickvigilante
fix: gate port-forward usage reporting and rename app name
014c5e8
Rename codersdk.UsageAppNamePortForward to UsageAppNamePortForwarding
(wire value port_forwarding) to match the arbitrary app names RFC's
connection-method name, per review.

Report port-forwarding usage only while a forwarded connection is
open, instead of on a fixed one-minute ticker regardless of activity.
An idle coder port-forward with nothing connected no longer counts as
an active session. A final report runs on shutdown so a session that
starts and ends between ticks is not lost.

Update TestPortForward_UpdateUsage: the no-connection case now expects
no session count, and a new case opens a forwarded connection and
expects a port_forwarding session.
nickvigilante added 2 commits October 2, 2026 13:53
@nickvigilante
fix: register port_forwarding family and tighten port-forward usage
37642b5
- Register a port_forwarding app family so sessions show as Port
  Forwarding instead of unknown. The deployment banner totals it
  under Other.
- Count only TCP connections. UDP forwards never see EOF, so one
  datagram kept the session active until exit.
- Report once at startup so the last-used bump isn't delayed a minute.
- Move the idle/active decision into portForwardUsage.request and
  table-test it.

Linear: CLI-31
@nickvigilante
refactor(cli): simplify port-forward usage reporting
a55b934
The usage tracker now returns a bool and the reporter builds the SDK request. The reporter's stop func cancels its own context before waiting, so callers no longer need to cancel first.
@nickvigilante
nickvigilante force-pushed the vigilante/cli-31-cli-port-forward-calls-deprecated branch from b205b39 to a55b934 Compare October 2, 2026 14:00
@nickvigilante
nickvigilante merged commit 1ad46e4 into main Oct 2, 2026
56 of 58 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@nickvigilante
nickvigilante deleted the vigilante/cli-31-cli-port-forward-calls-deprecated branch October 2, 2026 15:05
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

@chatgpt-codex-connector chatgpt-codex-connector[bot] chatgpt-codex-connector[bot] left review comments
@EhabY EhabY EhabY approved these changes
@matifali matifali Awaiting requested review from matifali

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@nickvigilante nickvigilante

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@nickvigilante @matifali @EhabY
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.