Skip to content

fix: apply etac rules to getNodeByID REST API#2978

Merged
ddlees merged 8 commits into
mainfrom
bed-8945
Jul 14, 2026
Merged

fix: apply etac rules to getNodeByID REST API#2978
ddlees merged 8 commits into
mainfrom
bed-8945

Conversation

@ddlees

@ddlees ddlees commented Jul 10, 2026

Copy link
Copy Markdown
Member

Description

  • Apply ETAC filter to getNodeByID REST API when enabled

Motivation and Context

Resolves BED-8945

How Has This Been Tested?

Added and updated all unit tests in graphdb onion

Screenshots (optional):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)

Checklist:

Summary by CodeRabbit

  • New Features
    • Added node-level access controls using environment-targeted access control (ETAC), enforced on node lookup.
  • Bug Fixes
    • Restricted node information is no longer returned when access is denied, including cases with missing or malformed environment identifiers.
    • Denied requests now consistently return 403 Forbidden with message "forbidden".
  • Tests
    • Added/updated coverage for ETAC storage and authorization logic, handler 403 behavior, and E2E requests with authenticated context.

@ddlees ddlees self-assigned this Jul 10, 2026
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds ETAC-backed node authorization, environment ID extraction, handler enforcement with 403 responses, user-resolution contracts, and dependency wiring through GraphDB registration.

Changes

Graph node authorization

Layer / File(s) Summary
User resolution contracts
server/users/users.go, server/users/mocks/*
Defines user and request resolver interfaces, request-context resolution, and generated mocks.
ETAC access service
server/etac/...
Adds ETAC registration, database access-control retrieval, authorization logic, tests, and generated mocks.
Node authorization logic
server/graphdb/internal/services/node.go, server/graphdb/internal/authz/*
Nodes expose environment identifiers, and NodeAuthorizer evaluates authentication and ETAC access.
Handler enforcement and registration wiring
server/graphdb/internal/handlers/*, server/graphdb/internal/routes/routes_test.go, server/modules/*, server/graphdb/graphdb.go, server/graphdb/graphdb_e2e_test.go
Handlers enforce authorization with 403 forbidden, while module and E2E wiring supplies DogTags, ETAC, and authenticated request context.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant GetNodeByID
  participant GraphDB
  participant NodeAuthorizer
  participant ETACService
  participant AppDB
  Client->>GetNodeByID: Request node by ID
  GetNodeByID->>GraphDB: Load node
  GraphDB-->>GetNodeByID: Node
  GetNodeByID->>NodeAuthorizer: CanAccessNode(context, node)
  NodeAuthorizer->>ETACService: CheckUserAccess(user, environmentID)
  ETACService->>AppDB: Query allowed environments
  AppDB-->>ETACService: Environment access records
  ETACService-->>NodeAuthorizer: Access decision
  NodeAuthorizer-->>GetNodeByID: Allow or deny
  GetNodeByID-->>Client: 200 node or 403 forbidden
Loading

Possibly related PRs

  • SpecterOps/BloodHound#2930: Adds the GraphDB node-by-ID endpoint and its handler/test scaffolding used by this authorization flow.

Suggested labels: bug, api, enhancement, go

Suggested reviewers: superlinkx, LawsonWillard, urangel

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.89% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: applying ETAC rules to getNodeByID.
Description check ✅ Passed The description matches the template with all required sections, issue reference, test summary, type, and checklist.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bed-8945

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added api A pull request containing changes affecting the API code. bug Something isn't working go Pull requests that update go code infrastructure A pull request containing changes affecting the infrastructure code. labels Jul 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/graphdb/internal/handlers/handlers.go`:
- Line 45: Update the remaining NewHandlersContainer caller in
graphdb_e2e_test.go to pass a NodeAuthorizer implementation, matching the setup
used by the updated handler tests, so the server/graphdb test package compiles
with the new constructor signature.

In `@server/graphdb/internal/handlers/mocks/nodeauthorizer.go`:
- Line 1: Update the mockery configuration or template used to generate
nodeauthorizer mocks so output includes LICENSE.header, uses the repository’s
approved receiver naming instead of _m/_mock, and replaces interface{} with the
preferred Go type syntax in helper signatures; regenerate nodeauthorizer.go and
verify the generated file follows repository Go policies.

In `@server/graphdb/internal/routes/routes_test.go`:
- Around line 48-53: The remaining handlers.NewHandlersContainer call in
graphdb_e2e_test.go must be updated to match its new two-argument signature.
Pass the existing services.NewService(store) GraphDB value and provide an
appropriate NodeAuthorizer implementation or mock, consistent with the setup in
routes_test.go, so the test typechecks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 5ec9909a-f796-4d84-8cda-68f11f46556a

📥 Commits

Reviewing files that changed from the base of the PR and between 275141e and a0ac8dc.

📒 Files selected for processing (12)
  • server/graphdb/graphdb.go
  • server/graphdb/internal/authz/authorizer.go
  • server/graphdb/internal/authz/authorizer_test.go
  • server/graphdb/internal/handlers/handlers.go
  • server/graphdb/internal/handlers/mocks/nodeauthorizer.go
  • server/graphdb/internal/handlers/node.go
  • server/graphdb/internal/handlers/node_test.go
  • server/graphdb/internal/handlers/relationship_test.go
  • server/graphdb/internal/routes/routes_test.go
  • server/graphdb/internal/services/node.go
  • server/modules/modules.go
  • server/modules/modules_test.go

Comment thread server/graphdb/internal/handlers/handlers.go
Comment thread server/graphdb/internal/handlers/mocks/nodeauthorizer.go
Comment thread server/graphdb/internal/routes/routes_test.go
Rewrote authorizer to use the ETAC slice rather than the old helper

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/graphdb/internal/authz/authorizer_test.go (1)

80-198: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add a test case for the ETAC error path.

None of the table cases mock CheckUserAccess to return an error, so the deny-on-error branch (authorizer.go lines 51-53: log + return false) is untested. Given this is the default-deny security path, it's worth covering explicitly.

✅ Suggested additional case
{
    name: "ETAC service error should be denied",
    ctx:  ctxWithUser(userWithEnvironments(false, testDomainSID)),
    node: newTestNode(ad.Entity.String(), ad.DomainSID.String(), testDomainSID),
    want: false,
    mockSetup: func(m *etacMocks.MockService, ctx context.Context, node services.Node) {
        m.EXPECT().CheckUserAccess(ctx, &model.User{
            AllEnvironments: false,
            EnvironmentTargetedAccessControl: []model.EnvironmentTargetedAccessControl{
                {EnvironmentID: testDomainSID},
            },
        }, testDomainSID).Return(false, errors.New("etac unavailable"))
    },
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/graphdb/internal/authz/authorizer_test.go` around lines 80 - 198, Add
a table entry to TestNodeAuthorizer_CanAccessNode named “ETAC service error
should be denied,” using the existing user, AD node, and testDomainSID setup;
configure CheckUserAccess to return false with an errors.New error and assert
want is false, importing errors if needed.
🧹 Nitpick comments (3)
server/etac/internal/services/service.go (2)

73-76: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Doc comment casing doesn't match the unexported identifier.

The comment refers to ShouldFilterForETAC but the method is unexported shouldFilterForETAC.

✏️ Fix comment casing
-// ShouldFilterForETAC determines whether ETAC filtering should be applied based
+// shouldFilterForETAC determines whether ETAC filtering should be applied based
 // on the feature flag and the user's environment access. Filtering is skipped
 // when the feature is disabled or when the user has access to all environments.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/etac/internal/services/service.go` around lines 73 - 76, Update the
doc comment above shouldFilterForETAC to begin with the exact unexported method
name, shouldFilterForETAC, instead of ShouldFilterForETAC.

86-89: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Doc comment casing doesn't match the exported identifier; also reconsider exporting this method solely for test access.

The comment says checkUserAccessToEnvironments but the method is exported as CheckUserAccessToEnvironments. Additionally, this method appears exported only so service_test.go (external services_test package) can exercise the otherwise-unreachable all-environments short-circuit branch — this expands the public API surface for test convenience. Consider keeping it unexported and covering that branch via an internal package services test file instead, per the coding guideline that internal-logic tests may live in the same package as the code under test.

✏️ Fix comment casing (minimal fix if keeping it exported)
-// checkUserAccessToEnvironments reports whether the user is permitted to access
+// CheckUserAccessToEnvironments reports whether the user is permitted to access
 // every environment in the supplied list. Users with access to all environments
 // are always permitted.
As per coding guidelines, "If a test file is testing internal (non-exported) logic, the file may be included in the same package as the code being tested."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/etac/internal/services/service.go` around lines 86 - 89, Keep
CheckUserAccessToEnvironments unexported as checkUserAccessToEnvironments unless
it is genuinely required by production callers, and move the short-circuit
coverage into an internal package services test so service_test.go does not
require the exported API. Update all internal call sites and tests accordingly;
if the method must remain exported, change its doc comment to begin with
CheckUserAccessToEnvironments.

Source: Coding guidelines

server/etac/internal/appdb/appdb_test.go (1)

82-90: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Coverage gap: the row-scan error branch (fmt.Errorf("reading rows: ...")) is untested.

WillReturnError fails at Query, never reaching pgx.CollectRows/RowToStructByName. Consider a case that returns malformed rows (e.g., wrong column types) to exercise that wrap path, especially once switched to %w.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/etac/internal/appdb/appdb_test.go` around lines 82 - 90, Extend the
“propagates database errors” tests for
GetEnvironmentTargetedAccessControlForUser with a malformed successful query
result that causes pgx.CollectRows/RowToStructByName to fail during scanning,
then assert the returned error wraps the scan error (including via errors.Is if
applicable) and verify pool expectations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/etac/internal/appdb/appdb.go`:
- Around line 82-88: In the row collection error handling within the appdb query
function, change the fmt.Errorf wrapper from the non-wrapping %s verb to %w so
callers can inspect the underlying error via errors.Is or errors.As, matching
the existing Query error behavior.

In `@server/graphdb/internal/authz/authorizer.go`:
- Around line 34-38: Update the e2e harness setup in graphdb_e2e_test.go to
construct a NodeAuthorizer via NewNodeAuthorizer using the available ETAC
service, then pass it to handlers.NewHandlersContainer alongside
services.NewService(store), matching the constructor’s new signature.

---

Outside diff comments:
In `@server/graphdb/internal/authz/authorizer_test.go`:
- Around line 80-198: Add a table entry to TestNodeAuthorizer_CanAccessNode
named “ETAC service error should be denied,” using the existing user, AD node,
and testDomainSID setup; configure CheckUserAccess to return false with an
errors.New error and assert want is false, importing errors if needed.

---

Nitpick comments:
In `@server/etac/internal/appdb/appdb_test.go`:
- Around line 82-90: Extend the “propagates database errors” tests for
GetEnvironmentTargetedAccessControlForUser with a malformed successful query
result that causes pgx.CollectRows/RowToStructByName to fail during scanning,
then assert the returned error wraps the scan error (including via errors.Is if
applicable) and verify pool expectations.

In `@server/etac/internal/services/service.go`:
- Around line 73-76: Update the doc comment above shouldFilterForETAC to begin
with the exact unexported method name, shouldFilterForETAC, instead of
ShouldFilterForETAC.
- Around line 86-89: Keep CheckUserAccessToEnvironments unexported as
checkUserAccessToEnvironments unless it is genuinely required by production
callers, and move the short-circuit coverage into an internal package services
test so service_test.go does not require the exported API. Update all internal
call sites and tests accordingly; if the method must remain exported, change its
doc comment to begin with CheckUserAccessToEnvironments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: d107f7df-3fbc-4fee-a8e0-32224fc625b7

📥 Commits

Reviewing files that changed from the base of the PR and between a0ac8dc and 880ed73.

📒 Files selected for processing (15)
  • server/etac/etac.go
  • server/etac/internal/appdb/appdb.go
  • server/etac/internal/appdb/appdb_integration_test.go
  • server/etac/internal/appdb/appdb_test.go
  • server/etac/internal/services/mocks/appdatabase.go
  • server/etac/internal/services/service.go
  • server/etac/internal/services/service_test.go
  • server/etac/mocks/service.go
  • server/graphdb/graphdb.go
  • server/graphdb/internal/authz/authorizer.go
  • server/graphdb/internal/authz/authorizer_test.go
  • server/graphdb/internal/handlers/mocks/nodeauthorizer.go
  • server/users/mocks/resolver.go
  • server/users/mocks/user.go
  • server/users/users.go
✅ Files skipped from review due to trivial changes (5)
  • server/users/mocks/resolver.go
  • server/graphdb/internal/handlers/mocks/nodeauthorizer.go
  • server/users/mocks/user.go
  • server/etac/mocks/service.go
  • server/etac/internal/services/mocks/appdatabase.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/graphdb/graphdb.go

Comment on lines +82 to +88
accessControlList, err := pgx.CollectRows(rows, func(row pgx.CollectableRow) (services.EnvironmentTargetedAccessControl, error) {
scanned, err := pgx.RowToStructByName[etacRow](row)
return toEnvironmentTargetedAccessControl(scanned), err
})
if err != nil {
return nil, fmt.Errorf("reading rows: %s", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use %w to preserve the error chain when wrapping row-scan errors.

fmt.Errorf("reading rows: %s", err) discards the underlying error, so callers can't errors.Is/errors.As against it — unlike the raw Query error returned a few lines above.

🐛 Proposed fix
-		return nil, fmt.Errorf("reading rows: %s", err)
+		return nil, fmt.Errorf("reading rows: %w", err)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
accessControlList, err := pgx.CollectRows(rows, func(row pgx.CollectableRow) (services.EnvironmentTargetedAccessControl, error) {
scanned, err := pgx.RowToStructByName[etacRow](row)
return toEnvironmentTargetedAccessControl(scanned), err
})
if err != nil {
return nil, fmt.Errorf("reading rows: %s", err)
}
accessControlList, err := pgx.CollectRows(rows, func(row pgx.CollectableRow) (services.EnvironmentTargetedAccessControl, error) {
scanned, err := pgx.RowToStructByName[etacRow](row)
return toEnvironmentTargetedAccessControl(scanned), err
})
if err != nil {
return nil, fmt.Errorf("reading rows: %w", err)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/etac/internal/appdb/appdb.go` around lines 82 - 88, In the row
collection error handling within the appdb query function, change the fmt.Errorf
wrapper from the non-wrapping %s verb to %w so callers can inspect the
underlying error via errors.Is or errors.As, matching the existing Query error
behavior.

Comment thread server/graphdb/internal/authz/authorizer.go
@coderabbitai coderabbitai Bot removed the infrastructure A pull request containing changes affecting the infrastructure code. label Jul 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
server/graphdb/graphdb_e2e_test.go (2)

106-119: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No e2e coverage for the 403 forbidden path introduced by this PR.

withAuthenticatedUser always injects a user with AllEnvironments: true, so CanAccessNode always allows access in this harness. TestGetNodeByID only exercises 200/400/404; there is no test asserting GetNodeByID returns 403 when the ETAC authorizer denies access — the exact behavior this PR wires end-to-end.

Consider adding a sub-middleware/test case using a restricted user (or a second harness variant) to assert the 403 response from s.nodeAuthorizer.CanAccessNode denial, since unit tests on NodeAuthorizer don't verify the full HTTP-level enforcement.

Also applies to: 291-359

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/graphdb/graphdb_e2e_test.go` around lines 106 - 119, The graph
database e2e tests lack HTTP-level coverage for authorization denial. Add a
restricted-user middleware or harness variant alongside withAuthenticatedUser,
configure the ETAC authorizer to deny access, and extend TestGetNodeByID with a
case asserting GetNodeByID returns HTTP 403.

160-163: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hoist new variable declarations into a var(...) block.

etacService, nodeAuthorizer, and handlerSet aren't error-checked immediately, so they can be grouped into a var(...) block per the repo's Go style.

♻️ Proposed grouping
-	store := appdb.NewStore(graphDatabase, dbPool)
-	etacService := etac.Register(dbPool, dogtags.NewDefaultService())
-	nodeAuthorizer := authz.NewNodeAuthorizer(etacService)
-	handlerSet := handlers.NewHandlersContainer(services.NewService(store), nodeAuthorizer)
+	var (
+		store          = appdb.NewStore(graphDatabase, dbPool)
+		etacService    = etac.Register(dbPool, dogtags.NewDefaultService())
+		nodeAuthorizer = authz.NewNodeAuthorizer(etacService)
+		handlerSet     = handlers.NewHandlersContainer(services.NewService(store), nodeAuthorizer)
+	)
As per coding guidelines, "Group variable initializations in a `var ( ... )` block and hoist them to the top of the function when possible."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/graphdb/graphdb_e2e_test.go` around lines 160 - 163, Group the
`etacService`, `nodeAuthorizer`, and `handlerSet` declarations into a single
var(...) block near the top of the containing test function, preserving their
existing initialization expressions and dependency order; leave the
error-checked `store` initialization separate if needed.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@server/graphdb/graphdb_e2e_test.go`:
- Around line 106-119: The graph database e2e tests lack HTTP-level coverage for
authorization denial. Add a restricted-user middleware or harness variant
alongside withAuthenticatedUser, configure the ETAC authorizer to deny access,
and extend TestGetNodeByID with a case asserting GetNodeByID returns HTTP 403.
- Around line 160-163: Group the `etacService`, `nodeAuthorizer`, and
`handlerSet` declarations into a single var(...) block near the top of the
containing test function, preserving their existing initialization expressions
and dependency order; leave the error-checked `store` initialization separate if
needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 4f85cb5a-9031-4236-9af4-eac0ccdebb0b

📥 Commits

Reviewing files that changed from the base of the PR and between 880ed73 and 20e1f2a.

📒 Files selected for processing (2)
  • server/graphdb/graphdb_e2e_test.go
  • server/graphdb/internal/authz/authorizer_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/graphdb/internal/authz/authorizer_test.go

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jul 10, 2026
Comment thread server/graphdb/internal/handlers/node.go

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The ETAC service has been migrated from BHE

Comment thread server/users/users.go
@@ -0,0 +1,61 @@
// Copyright 2026 Specter Ops, Inc.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The user service has been migrated from BHE

Comment thread server/modules/modules.go

@urangel urangel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thank you both for the huge lift! @ddlees and @superlinkx

@ddlees
ddlees merged commit c346002 into main Jul 14, 2026
12 checks passed
@ddlees
ddlees deleted the bed-8945 branch July 14, 2026 20:07
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 14, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

api A pull request containing changes affecting the API code. bug Something isn't working enhancement New feature or request go Pull requests that update go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants