mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
feat(coderd/database): add boundary_sessions and boundary_logs tables (#25441)
RFC: [Bridge ↔ Boundaries Correlation RFC](https://www.notion.so/coderhq/Gateway-and-Firewall-Correlation-RFC-31ad579be592803aa8b3d48348ccdde9) Add up/down migrations and matching sqlc queries for persisting Boundary audit events, as specified in the Bridge/Boundaries Correlation RFC. **Tables:** - `boundary_sessions`: session metadata with `workspace_agent_id` FK, `confined_process_name`, and timestamps (`started_at`, `updated_at`). ID is externally supplied by the Boundary process (no DB-side default). Created lazily when the first log for a session arrives. - `boundary_logs`: individual audit events with `session_id` FK, `sequence_number` (INT, primary ordering key), protocol/method/detail fields, and `matched_rule` (nullable; non-NULL implies allowed). **Indexes (per RFC):** - `(session_id, sequence_number)` for the ordering query path - `(captured_at)` for the retention purge path **Queries:** - `InsertBoundarySession` / `GetBoundarySessionByID` - `InsertBoundaryLog` / `GetBoundaryLogByID` - `ListBoundaryLogsBySessionID` with nullable `seq_after`/`seq_before` exclusive bounds for fetching events between two known interception sequence numbers - `DeleteOldBoundaryLogs` with row limit to avoid long-running transactions **Also includes:** dbgen helpers (`BoundarySession`, `BoundaryLog`), dbauthz implementations (reads gated on `ResourceAuditLog`, deletes on `ResourceSystem`), and all generated wrappers (dbmock, dbmetrics). No callers yet. A follow-up PR will add the dedicated `boundary_log` RBAC resource type. > Generated by Coder Agents
This commit is contained in:
@@ -12,6 +12,7 @@ const (
|
||||
CheckAiModelPricesOutputPriceCheck CheckConstraint = "ai_model_prices_output_price_check" // ai_model_prices
|
||||
CheckAiProvidersNameCheck CheckConstraint = "ai_providers_name_check" // ai_providers
|
||||
CheckAPIKeysAllowListNotEmpty CheckConstraint = "api_keys_allow_list_not_empty" // api_keys
|
||||
CheckBoundaryLogsSequenceNumberCheck CheckConstraint = "boundary_logs_sequence_number_check" // boundary_logs
|
||||
CheckChatModelConfigsAiProviderRequiredWhenActive CheckConstraint = "chat_model_configs_ai_provider_required_when_active" // chat_model_configs
|
||||
CheckChatModelConfigsCompressionThresholdCheck CheckConstraint = "chat_model_configs_compression_threshold_check" // chat_model_configs
|
||||
CheckChatModelConfigsContextLimitCheck CheckConstraint = "chat_model_configs_context_limit_check" // chat_model_configs
|
||||
|
||||
@@ -2161,6 +2161,14 @@ func (q *querier) DeleteOldAuditLogs(ctx context.Context, arg database.DeleteOld
|
||||
return q.db.DeleteOldAuditLogs(ctx, arg)
|
||||
}
|
||||
|
||||
// TODO (PR #24810): Replace rbac.ResourceSystem with dedicated boundary_log resource type.
|
||||
func (q *querier) DeleteOldBoundaryLogs(ctx context.Context, arg database.DeleteOldBoundaryLogsParams) (int64, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceSystem); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return q.db.DeleteOldBoundaryLogs(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) DeleteOldChatDebugRuns(ctx context.Context, arg database.DeleteOldChatDebugRunsParams) (int64, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceSystem); err != nil {
|
||||
return 0, err
|
||||
@@ -2742,6 +2750,22 @@ func (q *querier) GetAuthorizationUserRoles(ctx context.Context, userID uuid.UUI
|
||||
return q.db.GetAuthorizationUserRoles(ctx, userID)
|
||||
}
|
||||
|
||||
// TODO (PR #24810): Replace rbac.ResourceAuditLog with dedicated boundary_log resource type.
|
||||
func (q *querier) GetBoundaryLogByID(ctx context.Context, id uuid.UUID) (database.BoundaryLog, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceAuditLog); err != nil {
|
||||
return database.BoundaryLog{}, err
|
||||
}
|
||||
return q.db.GetBoundaryLogByID(ctx, id)
|
||||
}
|
||||
|
||||
// TODO (PR #24810): Replace rbac.ResourceAuditLog with dedicated boundary_log resource type.
|
||||
func (q *querier) GetBoundarySessionByID(ctx context.Context, id uuid.UUID) (database.BoundarySession, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceAuditLog); err != nil {
|
||||
return database.BoundarySession{}, err
|
||||
}
|
||||
return q.db.GetBoundarySessionByID(ctx, id)
|
||||
}
|
||||
|
||||
func (q *querier) GetChatACLByID(ctx context.Context, id uuid.UUID) (database.GetChatACLByIDRow, error) {
|
||||
chat, err := q.db.GetChatByID(ctx, id)
|
||||
if err != nil {
|
||||
@@ -5413,6 +5437,16 @@ func (q *querier) InsertAuditLog(ctx context.Context, arg database.InsertAuditLo
|
||||
return insert(q.log, q.auth, rbac.ResourceAuditLog, q.db.InsertAuditLog)(ctx, arg)
|
||||
}
|
||||
|
||||
// TODO (PR #24810): Replace rbac.ResourceAuditLog with dedicated boundary_log resource type.
|
||||
func (q *querier) InsertBoundaryLog(ctx context.Context, arg database.InsertBoundaryLogParams) (database.BoundaryLog, error) {
|
||||
return insert(q.log, q.auth, rbac.ResourceAuditLog, q.db.InsertBoundaryLog)(ctx, arg)
|
||||
}
|
||||
|
||||
// TODO (PR #24810): Replace rbac.ResourceAuditLog with dedicated boundary_log resource type.
|
||||
func (q *querier) InsertBoundarySession(ctx context.Context, arg database.InsertBoundarySessionParams) (database.BoundarySession, error) {
|
||||
return insert(q.log, q.auth, rbac.ResourceAuditLog, q.db.InsertBoundarySession)(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertChat(ctx context.Context, arg database.InsertChatParams) (database.Chat, error) {
|
||||
return insert(q.log, q.auth, rbac.ResourceChat.WithOwner(arg.OwnerID.String()).InOrg(arg.OrganizationID), q.db.InsertChat)(ctx, arg)
|
||||
}
|
||||
@@ -6126,6 +6160,14 @@ func (q *querier) ListAIBridgeUserPromptsByInterceptionIDs(ctx context.Context,
|
||||
return q.db.ListAIBridgeUserPromptsByInterceptionIDs(ctx, interceptionIDs)
|
||||
}
|
||||
|
||||
// TODO (PR #24810): Replace rbac.ResourceAuditLog with dedicated boundary_log resource type.
|
||||
func (q *querier) ListBoundaryLogsBySessionID(ctx context.Context, arg database.ListBoundaryLogsBySessionIDParams) ([]database.BoundaryLog, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceAuditLog); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.db.ListBoundaryLogsBySessionID(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) ListChatUsageLimitGroupOverrides(ctx context.Context) ([]database.ListChatUsageLimitGroupOverridesRow, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceDeploymentConfig); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -440,6 +440,38 @@ func (s *MethodTestSuite) TestAuditLogs() {
|
||||
}))
|
||||
}
|
||||
|
||||
// TODO (PR #24810): These RBAC assertions use placeholder resource types.
|
||||
// They will be updated when the dedicated boundary_log resource type is added.
|
||||
func (s *MethodTestSuite) TestBoundaryLogs() {
|
||||
s.Run("InsertBoundarySession", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
|
||||
arg := database.InsertBoundarySessionParams{}
|
||||
dbm.EXPECT().InsertBoundarySession(gomock.Any(), arg).Return(database.BoundarySession{}, nil).AnyTimes()
|
||||
check.Args(arg).Asserts(rbac.ResourceAuditLog, policy.ActionCreate)
|
||||
}))
|
||||
s.Run("GetBoundarySessionByID", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
|
||||
dbm.EXPECT().GetBoundarySessionByID(gomock.Any(), uuid.Nil).Return(database.BoundarySession{}, nil).AnyTimes()
|
||||
check.Args(uuid.Nil).Asserts(rbac.ResourceAuditLog, policy.ActionRead)
|
||||
}))
|
||||
s.Run("InsertBoundaryLog", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
|
||||
arg := database.InsertBoundaryLogParams{}
|
||||
dbm.EXPECT().InsertBoundaryLog(gomock.Any(), arg).Return(database.BoundaryLog{}, nil).AnyTimes()
|
||||
check.Args(arg).Asserts(rbac.ResourceAuditLog, policy.ActionCreate)
|
||||
}))
|
||||
s.Run("GetBoundaryLogByID", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
|
||||
dbm.EXPECT().GetBoundaryLogByID(gomock.Any(), uuid.Nil).Return(database.BoundaryLog{}, nil).AnyTimes()
|
||||
check.Args(uuid.Nil).Asserts(rbac.ResourceAuditLog, policy.ActionRead)
|
||||
}))
|
||||
s.Run("ListBoundaryLogsBySessionID", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
|
||||
arg := database.ListBoundaryLogsBySessionIDParams{}
|
||||
dbm.EXPECT().ListBoundaryLogsBySessionID(gomock.Any(), arg).Return([]database.BoundaryLog{}, nil).AnyTimes()
|
||||
check.Args(arg).Asserts(rbac.ResourceAuditLog, policy.ActionRead)
|
||||
}))
|
||||
s.Run("DeleteOldBoundaryLogs", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
|
||||
dbm.EXPECT().DeleteOldBoundaryLogs(gomock.Any(), database.DeleteOldBoundaryLogsParams{}).Return(int64(0), nil).AnyTimes()
|
||||
check.Args(database.DeleteOldBoundaryLogsParams{}).Asserts(rbac.ResourceSystem, policy.ActionDelete)
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *MethodTestSuite) TestConnectionLogs() {
|
||||
s.Run("BatchUpsertConnectionLogs", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) {
|
||||
arg := database.BatchUpsertConnectionLogsParams{}
|
||||
|
||||
@@ -453,6 +453,34 @@ func ConnectionLog(t testing.TB, db database.Store, seed database.UpsertConnecti
|
||||
return database.ConnectionLog{} // unreachable
|
||||
}
|
||||
|
||||
func BoundarySession(t testing.TB, db database.Store, seed database.BoundarySession) database.BoundarySession {
|
||||
session, err := db.InsertBoundarySession(genCtx, database.InsertBoundarySessionParams{
|
||||
ID: takeFirst(seed.ID, uuid.New()),
|
||||
WorkspaceAgentID: takeFirst(seed.WorkspaceAgentID, uuid.New()),
|
||||
ConfinedProcessName: takeFirst(seed.ConfinedProcessName, "claude-code"),
|
||||
StartedAt: takeFirst(seed.StartedAt, dbtime.Now()),
|
||||
UpdatedAt: takeFirst(seed.UpdatedAt, dbtime.Now()),
|
||||
})
|
||||
require.NoError(t, err, "insert boundary session")
|
||||
return session
|
||||
}
|
||||
|
||||
func BoundaryLog(t testing.TB, db database.Store, seed database.BoundaryLog) database.BoundaryLog {
|
||||
log, err := db.InsertBoundaryLog(genCtx, database.InsertBoundaryLogParams{
|
||||
ID: takeFirst(seed.ID, uuid.New()),
|
||||
SessionID: seed.SessionID,
|
||||
SequenceNumber: takeFirst(seed.SequenceNumber, 0),
|
||||
CapturedAt: takeFirst(seed.CapturedAt, dbtime.Now()),
|
||||
CreatedAt: takeFirst(seed.CreatedAt, dbtime.Now()),
|
||||
Proto: takeFirst(seed.Proto, "http"),
|
||||
Method: takeFirst(seed.Method, "GET"),
|
||||
Detail: takeFirst(seed.Detail, "https://example.com"),
|
||||
MatchedRule: seed.MatchedRule,
|
||||
})
|
||||
require.NoError(t, err, "insert boundary log")
|
||||
return log
|
||||
}
|
||||
|
||||
func Template(t testing.TB, db database.Store, seed database.Template) database.Template {
|
||||
id := takeFirst(seed.ID, uuid.New())
|
||||
if seed.GroupACL == nil {
|
||||
|
||||
@@ -657,6 +657,14 @@ func (m queryMetricsStore) DeleteOldAuditLogs(ctx context.Context, arg database.
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) DeleteOldBoundaryLogs(ctx context.Context, arg database.DeleteOldBoundaryLogsParams) (int64, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.DeleteOldBoundaryLogs(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("DeleteOldBoundaryLogs").Observe(time.Since(start).Seconds())
|
||||
m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "DeleteOldBoundaryLogs").Inc()
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) DeleteOldChatDebugRuns(ctx context.Context, arg database.DeleteOldChatDebugRunsParams) (int64, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.DeleteOldChatDebugRuns(ctx, arg)
|
||||
@@ -1249,6 +1257,22 @@ func (m queryMetricsStore) GetAuthorizationUserRoles(ctx context.Context, userID
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetBoundaryLogByID(ctx context.Context, id uuid.UUID) (database.BoundaryLog, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetBoundaryLogByID(ctx, id)
|
||||
m.queryLatencies.WithLabelValues("GetBoundaryLogByID").Observe(time.Since(start).Seconds())
|
||||
m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "GetBoundaryLogByID").Inc()
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetBoundarySessionByID(ctx context.Context, id uuid.UUID) (database.BoundarySession, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetBoundarySessionByID(ctx, id)
|
||||
m.queryLatencies.WithLabelValues("GetBoundarySessionByID").Observe(time.Since(start).Seconds())
|
||||
m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "GetBoundarySessionByID").Inc()
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetChatACLByID(ctx context.Context, id uuid.UUID) (database.GetChatACLByIDRow, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetChatACLByID(ctx, id)
|
||||
@@ -3721,6 +3745,22 @@ func (m queryMetricsStore) InsertAuditLog(ctx context.Context, arg database.Inse
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) InsertBoundaryLog(ctx context.Context, arg database.InsertBoundaryLogParams) (database.BoundaryLog, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.InsertBoundaryLog(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("InsertBoundaryLog").Observe(time.Since(start).Seconds())
|
||||
m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "InsertBoundaryLog").Inc()
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) InsertBoundarySession(ctx context.Context, arg database.InsertBoundarySessionParams) (database.BoundarySession, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.InsertBoundarySession(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("InsertBoundarySession").Observe(time.Since(start).Seconds())
|
||||
m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "InsertBoundarySession").Inc()
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) InsertChat(ctx context.Context, arg database.InsertChatParams) (database.Chat, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.InsertChat(ctx, arg)
|
||||
@@ -4361,6 +4401,14 @@ func (m queryMetricsStore) ListAIBridgeUserPromptsByInterceptionIDs(ctx context.
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) ListBoundaryLogsBySessionID(ctx context.Context, arg database.ListBoundaryLogsBySessionIDParams) ([]database.BoundaryLog, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.ListBoundaryLogsBySessionID(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("ListBoundaryLogsBySessionID").Observe(time.Since(start).Seconds())
|
||||
m.queryCounts.WithLabelValues(httpmw.ExtractHTTPRoute(ctx), httpmw.ExtractHTTPMethod(ctx), "ListBoundaryLogsBySessionID").Inc()
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) ListChatUsageLimitGroupOverrides(ctx context.Context) ([]database.ListChatUsageLimitGroupOverridesRow, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.ListChatUsageLimitGroupOverrides(ctx)
|
||||
|
||||
@@ -1102,6 +1102,21 @@ func (mr *MockStoreMockRecorder) DeleteOldAuditLogs(ctx, arg any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOldAuditLogs", reflect.TypeOf((*MockStore)(nil).DeleteOldAuditLogs), ctx, arg)
|
||||
}
|
||||
|
||||
// DeleteOldBoundaryLogs mocks base method.
|
||||
func (m *MockStore) DeleteOldBoundaryLogs(ctx context.Context, arg database.DeleteOldBoundaryLogsParams) (int64, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteOldBoundaryLogs", ctx, arg)
|
||||
ret0, _ := ret[0].(int64)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// DeleteOldBoundaryLogs indicates an expected call of DeleteOldBoundaryLogs.
|
||||
func (mr *MockStoreMockRecorder) DeleteOldBoundaryLogs(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOldBoundaryLogs", reflect.TypeOf((*MockStore)(nil).DeleteOldBoundaryLogs), ctx, arg)
|
||||
}
|
||||
|
||||
// DeleteOldChatDebugRuns mocks base method.
|
||||
func (m *MockStore) DeleteOldChatDebugRuns(ctx context.Context, arg database.DeleteOldChatDebugRunsParams) (int64, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -2310,6 +2325,36 @@ func (mr *MockStoreMockRecorder) GetAuthorizedWorkspacesAndAgentsByOwnerID(ctx,
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthorizedWorkspacesAndAgentsByOwnerID", reflect.TypeOf((*MockStore)(nil).GetAuthorizedWorkspacesAndAgentsByOwnerID), ctx, ownerID, prepared)
|
||||
}
|
||||
|
||||
// GetBoundaryLogByID mocks base method.
|
||||
func (m *MockStore) GetBoundaryLogByID(ctx context.Context, id uuid.UUID) (database.BoundaryLog, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBoundaryLogByID", ctx, id)
|
||||
ret0, _ := ret[0].(database.BoundaryLog)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetBoundaryLogByID indicates an expected call of GetBoundaryLogByID.
|
||||
func (mr *MockStoreMockRecorder) GetBoundaryLogByID(ctx, id any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBoundaryLogByID", reflect.TypeOf((*MockStore)(nil).GetBoundaryLogByID), ctx, id)
|
||||
}
|
||||
|
||||
// GetBoundarySessionByID mocks base method.
|
||||
func (m *MockStore) GetBoundarySessionByID(ctx context.Context, id uuid.UUID) (database.BoundarySession, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBoundarySessionByID", ctx, id)
|
||||
ret0, _ := ret[0].(database.BoundarySession)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetBoundarySessionByID indicates an expected call of GetBoundarySessionByID.
|
||||
func (mr *MockStoreMockRecorder) GetBoundarySessionByID(ctx, id any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBoundarySessionByID", reflect.TypeOf((*MockStore)(nil).GetBoundarySessionByID), ctx, id)
|
||||
}
|
||||
|
||||
// GetChatACLByID mocks base method.
|
||||
func (m *MockStore) GetChatACLByID(ctx context.Context, id uuid.UUID) (database.GetChatACLByIDRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -6989,6 +7034,36 @@ func (mr *MockStoreMockRecorder) InsertAuditLog(ctx, arg any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertAuditLog", reflect.TypeOf((*MockStore)(nil).InsertAuditLog), ctx, arg)
|
||||
}
|
||||
|
||||
// InsertBoundaryLog mocks base method.
|
||||
func (m *MockStore) InsertBoundaryLog(ctx context.Context, arg database.InsertBoundaryLogParams) (database.BoundaryLog, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InsertBoundaryLog", ctx, arg)
|
||||
ret0, _ := ret[0].(database.BoundaryLog)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// InsertBoundaryLog indicates an expected call of InsertBoundaryLog.
|
||||
func (mr *MockStoreMockRecorder) InsertBoundaryLog(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertBoundaryLog", reflect.TypeOf((*MockStore)(nil).InsertBoundaryLog), ctx, arg)
|
||||
}
|
||||
|
||||
// InsertBoundarySession mocks base method.
|
||||
func (m *MockStore) InsertBoundarySession(ctx context.Context, arg database.InsertBoundarySessionParams) (database.BoundarySession, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InsertBoundarySession", ctx, arg)
|
||||
ret0, _ := ret[0].(database.BoundarySession)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// InsertBoundarySession indicates an expected call of InsertBoundarySession.
|
||||
func (mr *MockStoreMockRecorder) InsertBoundarySession(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertBoundarySession", reflect.TypeOf((*MockStore)(nil).InsertBoundarySession), ctx, arg)
|
||||
}
|
||||
|
||||
// InsertChat mocks base method.
|
||||
func (m *MockStore) InsertChat(ctx context.Context, arg database.InsertChatParams) (database.Chat, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -8249,6 +8324,21 @@ func (mr *MockStoreMockRecorder) ListAuthorizedAIBridgeSessions(ctx, arg, prepar
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAuthorizedAIBridgeSessions", reflect.TypeOf((*MockStore)(nil).ListAuthorizedAIBridgeSessions), ctx, arg, prepared)
|
||||
}
|
||||
|
||||
// ListBoundaryLogsBySessionID mocks base method.
|
||||
func (m *MockStore) ListBoundaryLogsBySessionID(ctx context.Context, arg database.ListBoundaryLogsBySessionIDParams) ([]database.BoundaryLog, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ListBoundaryLogsBySessionID", ctx, arg)
|
||||
ret0, _ := ret[0].([]database.BoundaryLog)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ListBoundaryLogsBySessionID indicates an expected call of ListBoundaryLogsBySessionID.
|
||||
func (mr *MockStoreMockRecorder) ListBoundaryLogsBySessionID(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBoundaryLogsBySessionID", reflect.TypeOf((*MockStore)(nil).ListBoundaryLogsBySessionID), ctx, arg)
|
||||
}
|
||||
|
||||
// ListChatUsageLimitGroupOverrides mocks base method.
|
||||
func (m *MockStore) ListChatUsageLimitGroupOverrides(ctx context.Context) ([]database.ListChatUsageLimitGroupOverridesRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
Generated
+67
@@ -1377,6 +1377,57 @@ CREATE TABLE audit_logs (
|
||||
resource_icon text NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE boundary_logs (
|
||||
id uuid NOT NULL,
|
||||
session_id uuid NOT NULL,
|
||||
sequence_number integer NOT NULL,
|
||||
captured_at timestamp with time zone NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
proto text DEFAULT ''::text NOT NULL,
|
||||
method text DEFAULT ''::text NOT NULL,
|
||||
detail text DEFAULT ''::text NOT NULL,
|
||||
matched_rule text,
|
||||
CONSTRAINT boundary_logs_sequence_number_check CHECK ((sequence_number >= 0))
|
||||
);
|
||||
|
||||
COMMENT ON TABLE boundary_logs IS 'Persisted boundary audit events. Each row is a single audit event processed by a Boundary proxy.';
|
||||
|
||||
COMMENT ON COLUMN boundary_logs.session_id IS 'The session ID generated by the Boundary process on startup. Groups all events from one invocation.';
|
||||
|
||||
COMMENT ON COLUMN boundary_logs.sequence_number IS 'Monotonically increasing integer assigned by Boundary, starting at 0 per session. Primary ordering key when Boundary is in use.';
|
||||
|
||||
COMMENT ON COLUMN boundary_logs.captured_at IS 'When the log was sent to the DB.';
|
||||
|
||||
COMMENT ON COLUMN boundary_logs.created_at IS 'When the event happened on the workspace.';
|
||||
|
||||
COMMENT ON COLUMN boundary_logs.proto IS 'The protocol of the audited action. e.g. http, dns, git, fs.';
|
||||
|
||||
COMMENT ON COLUMN boundary_logs.method IS 'The operation within the protocol. e.g. GET/POST for http, clone for git, A for dns, read/write for fs.';
|
||||
|
||||
COMMENT ON COLUMN boundary_logs.detail IS 'Protocol-specific detail. e.g. the full URL for http, the hostname for dns, the path for fs.';
|
||||
|
||||
COMMENT ON COLUMN boundary_logs.matched_rule IS 'The allow-list rule that matched. NULL when the request was denied; non-NULL implies the request was allowed.';
|
||||
|
||||
CREATE TABLE boundary_sessions (
|
||||
id uuid NOT NULL,
|
||||
workspace_agent_id uuid NOT NULL,
|
||||
confined_process_name text NOT NULL,
|
||||
started_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE boundary_sessions IS 'Boundary session metadata. Each row represents a single invocation of a Boundary process wrapping a confined agent.';
|
||||
|
||||
COMMENT ON COLUMN boundary_sessions.id IS 'The unique session ID generated by the Boundary process on startup.';
|
||||
|
||||
COMMENT ON COLUMN boundary_sessions.workspace_agent_id IS 'The workspace agent that this Boundary session is associated with.';
|
||||
|
||||
COMMENT ON COLUMN boundary_sessions.confined_process_name IS 'Name of the confined process (e.g. claude-code, codex, copilot).';
|
||||
|
||||
COMMENT ON COLUMN boundary_sessions.started_at IS 'Time when the first log for this session was received by coderd.';
|
||||
|
||||
COMMENT ON COLUMN boundary_sessions.updated_at IS 'Time when the session was last updated.';
|
||||
|
||||
CREATE TABLE boundary_usage_stats (
|
||||
replica_id uuid NOT NULL,
|
||||
unique_workspaces_count bigint DEFAULT 0 NOT NULL,
|
||||
@@ -3614,6 +3665,12 @@ ALTER TABLE ONLY api_keys
|
||||
ALTER TABLE ONLY audit_logs
|
||||
ADD CONSTRAINT audit_logs_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY boundary_logs
|
||||
ADD CONSTRAINT boundary_logs_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY boundary_sessions
|
||||
ADD CONSTRAINT boundary_sessions_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY boundary_usage_stats
|
||||
ADD CONSTRAINT boundary_usage_stats_pkey PRIMARY KEY (replica_id);
|
||||
|
||||
@@ -4023,6 +4080,10 @@ CREATE INDEX idx_audit_log_user_id ON audit_logs USING btree (user_id);
|
||||
|
||||
CREATE INDEX idx_audit_logs_time_desc ON audit_logs USING btree ("time" DESC);
|
||||
|
||||
CREATE INDEX idx_boundary_logs_captured_at ON boundary_logs USING btree (captured_at);
|
||||
|
||||
CREATE INDEX idx_boundary_logs_session_seq ON boundary_logs USING btree (session_id, sequence_number);
|
||||
|
||||
CREATE INDEX idx_chat_debug_runs_chat_started ON chat_debug_runs USING btree (chat_id, started_at DESC);
|
||||
|
||||
CREATE UNIQUE INDEX idx_chat_debug_runs_id_chat ON chat_debug_runs USING btree (id, chat_id);
|
||||
@@ -4365,6 +4426,12 @@ ALTER TABLE ONLY aibridge_interceptions
|
||||
ALTER TABLE ONLY api_keys
|
||||
ADD CONSTRAINT api_keys_user_id_uuid_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY boundary_logs
|
||||
ADD CONSTRAINT boundary_logs_session_id_fkey FOREIGN KEY (session_id) REFERENCES boundary_sessions(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY boundary_sessions
|
||||
ADD CONSTRAINT boundary_sessions_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id);
|
||||
|
||||
ALTER TABLE ONLY chat_debug_runs
|
||||
ADD CONSTRAINT chat_debug_runs_chat_id_fkey FOREIGN KEY (chat_id) REFERENCES chats(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ const (
|
||||
ForeignKeyAiSeatStateUserID ForeignKeyConstraint = "ai_seat_state_user_id_fkey" // ALTER TABLE ONLY ai_seat_state ADD CONSTRAINT ai_seat_state_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
ForeignKeyAibridgeInterceptionsInitiatorID ForeignKeyConstraint = "aibridge_interceptions_initiator_id_fkey" // ALTER TABLE ONLY aibridge_interceptions ADD CONSTRAINT aibridge_interceptions_initiator_id_fkey FOREIGN KEY (initiator_id) REFERENCES users(id);
|
||||
ForeignKeyAPIKeysUserIDUUID ForeignKeyConstraint = "api_keys_user_id_uuid_fkey" // ALTER TABLE ONLY api_keys ADD CONSTRAINT api_keys_user_id_uuid_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
ForeignKeyBoundaryLogsSessionID ForeignKeyConstraint = "boundary_logs_session_id_fkey" // ALTER TABLE ONLY boundary_logs ADD CONSTRAINT boundary_logs_session_id_fkey FOREIGN KEY (session_id) REFERENCES boundary_sessions(id) ON DELETE CASCADE;
|
||||
ForeignKeyBoundarySessionsWorkspaceAgentID ForeignKeyConstraint = "boundary_sessions_workspace_agent_id_fkey" // ALTER TABLE ONLY boundary_sessions ADD CONSTRAINT boundary_sessions_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id);
|
||||
ForeignKeyChatDebugRunsChatID ForeignKeyConstraint = "chat_debug_runs_chat_id_fkey" // ALTER TABLE ONLY chat_debug_runs ADD CONSTRAINT chat_debug_runs_chat_id_fkey FOREIGN KEY (chat_id) REFERENCES chats(id) ON DELETE CASCADE;
|
||||
ForeignKeyChatDebugStepsChatID ForeignKeyConstraint = "chat_debug_steps_chat_id_fkey" // ALTER TABLE ONLY chat_debug_steps ADD CONSTRAINT chat_debug_steps_chat_id_fkey FOREIGN KEY (chat_id) REFERENCES chats(id) ON DELETE CASCADE;
|
||||
ForeignKeyChatDiffStatusesChatID ForeignKeyConstraint = "chat_diff_statuses_chat_id_fkey" // ALTER TABLE ONLY chat_diff_statuses ADD CONSTRAINT chat_diff_statuses_chat_id_fkey FOREIGN KEY (chat_id) REFERENCES chats(id) ON DELETE CASCADE;
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
DROP INDEX IF EXISTS idx_boundary_logs_captured_at;
|
||||
DROP INDEX IF EXISTS idx_boundary_logs_session_seq;
|
||||
DROP TABLE IF EXISTS boundary_logs;
|
||||
DROP TABLE IF EXISTS boundary_sessions;
|
||||
@@ -0,0 +1,43 @@
|
||||
CREATE TABLE boundary_sessions (
|
||||
id UUID PRIMARY KEY,
|
||||
workspace_agent_id UUID NOT NULL REFERENCES workspace_agents(id),
|
||||
confined_process_name TEXT NOT NULL,
|
||||
started_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE boundary_sessions IS 'Boundary session metadata. Each row represents a single invocation of a Boundary process wrapping a confined agent.';
|
||||
COMMENT ON COLUMN boundary_sessions.id IS 'The unique session ID generated by the Boundary process on startup.';
|
||||
COMMENT ON COLUMN boundary_sessions.workspace_agent_id IS 'The workspace agent that this Boundary session is associated with.';
|
||||
COMMENT ON COLUMN boundary_sessions.confined_process_name IS 'Name of the confined process (e.g. claude-code, codex, copilot).';
|
||||
COMMENT ON COLUMN boundary_sessions.started_at IS 'Time when the first log for this session was received by coderd.';
|
||||
COMMENT ON COLUMN boundary_sessions.updated_at IS 'Time when the session was last updated.';
|
||||
|
||||
CREATE TABLE boundary_logs (
|
||||
id UUID NOT NULL,
|
||||
session_id UUID NOT NULL REFERENCES boundary_sessions(id) ON DELETE CASCADE,
|
||||
sequence_number INT NOT NULL CHECK (sequence_number >= 0),
|
||||
captured_at TIMESTAMPTZ NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
proto TEXT NOT NULL DEFAULT '',
|
||||
method TEXT NOT NULL DEFAULT '',
|
||||
detail TEXT NOT NULL DEFAULT '',
|
||||
matched_rule TEXT,
|
||||
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE boundary_logs IS 'Persisted boundary audit events. Each row is a single audit event processed by a Boundary proxy.';
|
||||
COMMENT ON COLUMN boundary_logs.session_id IS 'The session ID generated by the Boundary process on startup. Groups all events from one invocation.';
|
||||
COMMENT ON COLUMN boundary_logs.sequence_number IS 'Monotonically increasing integer assigned by Boundary, starting at 0 per session. Primary ordering key when Boundary is in use.';
|
||||
COMMENT ON COLUMN boundary_logs.captured_at IS 'When the log was sent to the DB.';
|
||||
COMMENT ON COLUMN boundary_logs.created_at IS 'When the event happened on the workspace.';
|
||||
COMMENT ON COLUMN boundary_logs.proto IS 'The protocol of the audited action. e.g. http, dns, git, fs.';
|
||||
COMMENT ON COLUMN boundary_logs.method IS 'The operation within the protocol. e.g. GET/POST for http, clone for git, A for dns, read/write for fs.';
|
||||
COMMENT ON COLUMN boundary_logs.detail IS 'Protocol-specific detail. e.g. the full URL for http, the hostname for dns, the path for fs.';
|
||||
COMMENT ON COLUMN boundary_logs.matched_rule IS 'The allow-list rule that matched. NULL when the request was denied; non-NULL implies the request was allowed.';
|
||||
|
||||
-- Ordering query path: list events for a session, sorted by sequence number.
|
||||
CREATE INDEX idx_boundary_logs_session_seq ON boundary_logs (session_id, sequence_number);
|
||||
-- Retention purge path: delete old rows by capture time.
|
||||
CREATE INDEX idx_boundary_logs_captured_at ON boundary_logs (captured_at);
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
INSERT INTO boundary_sessions (
|
||||
id,
|
||||
workspace_agent_id,
|
||||
confined_process_name,
|
||||
started_at,
|
||||
updated_at
|
||||
) VALUES (
|
||||
'a1b2c3d4-e5f6-4890-abcd-ef1234567890',
|
||||
'45e89705-e09d-4850-bcec-f9a937f5d78d',
|
||||
'claude-code',
|
||||
'2026-04-01 10:00:00+00',
|
||||
'2026-04-01 10:00:00+00'
|
||||
);
|
||||
|
||||
INSERT INTO boundary_logs (
|
||||
id,
|
||||
session_id,
|
||||
sequence_number,
|
||||
captured_at,
|
||||
created_at,
|
||||
proto,
|
||||
method,
|
||||
detail,
|
||||
matched_rule
|
||||
) VALUES (
|
||||
'b2c3d4e5-f6a7-4901-bcde-f12345678901',
|
||||
'a1b2c3d4-e5f6-4890-abcd-ef1234567890',
|
||||
0,
|
||||
'2026-04-01 10:00:01+00',
|
||||
'2026-04-01 10:00:00+00',
|
||||
'http',
|
||||
'GET',
|
||||
'https://api.anthropic.com/v1/messages',
|
||||
'domain=api.anthropic.com'
|
||||
);
|
||||
@@ -4510,6 +4510,41 @@ type AuditLog struct {
|
||||
ResourceIcon string `db:"resource_icon" json:"resource_icon"`
|
||||
}
|
||||
|
||||
// Persisted boundary audit events. Each row is a single audit event processed by a Boundary proxy.
|
||||
type BoundaryLog struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
// The session ID generated by the Boundary process on startup. Groups all events from one invocation.
|
||||
SessionID uuid.UUID `db:"session_id" json:"session_id"`
|
||||
// Monotonically increasing integer assigned by Boundary, starting at 0 per session. Primary ordering key when Boundary is in use.
|
||||
SequenceNumber int32 `db:"sequence_number" json:"sequence_number"`
|
||||
// When the log was sent to the DB.
|
||||
CapturedAt time.Time `db:"captured_at" json:"captured_at"`
|
||||
// When the event happened on the workspace.
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
// The protocol of the audited action. e.g. http, dns, git, fs.
|
||||
Proto string `db:"proto" json:"proto"`
|
||||
// The operation within the protocol. e.g. GET/POST for http, clone for git, A for dns, read/write for fs.
|
||||
Method string `db:"method" json:"method"`
|
||||
// Protocol-specific detail. e.g. the full URL for http, the hostname for dns, the path for fs.
|
||||
Detail string `db:"detail" json:"detail"`
|
||||
// The allow-list rule that matched. NULL when the request was denied; non-NULL implies the request was allowed.
|
||||
MatchedRule sql.NullString `db:"matched_rule" json:"matched_rule"`
|
||||
}
|
||||
|
||||
// Boundary session metadata. Each row represents a single invocation of a Boundary process wrapping a confined agent.
|
||||
type BoundarySession struct {
|
||||
// The unique session ID generated by the Boundary process on startup.
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
// The workspace agent that this Boundary session is associated with.
|
||||
WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"`
|
||||
// Name of the confined process (e.g. claude-code, codex, copilot).
|
||||
ConfinedProcessName string `db:"confined_process_name" json:"confined_process_name"`
|
||||
// Time when the first log for this session was received by coderd.
|
||||
StartedAt time.Time `db:"started_at" json:"started_at"`
|
||||
// Time when the session was last updated.
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
// Per-replica boundary usage statistics for telemetry aggregation.
|
||||
type BoundaryUsageStat struct {
|
||||
// The unique identifier of the replica reporting stats.
|
||||
|
||||
@@ -152,6 +152,9 @@ type sqlcQuerier interface {
|
||||
// connection events (connect, disconnect, open, close) which are handled
|
||||
// separately by DeleteOldAuditLogConnectionEvents.
|
||||
DeleteOldAuditLogs(ctx context.Context, arg DeleteOldAuditLogsParams) (int64, error)
|
||||
// Deletes boundary logs older than the given time, bounded by a row limit
|
||||
// to avoid long-running transactions.
|
||||
DeleteOldBoundaryLogs(ctx context.Context, arg DeleteOldBoundaryLogsParams) (int64, error)
|
||||
// updated_at is the retention clock, so the window starts after the run
|
||||
// stops being written to.
|
||||
// Intentionally no finished_at IS NOT NULL guard: abandoned in-flight rows
|
||||
@@ -313,6 +316,8 @@ type sqlcQuerier interface {
|
||||
// This function returns roles for authorization purposes. Implied member roles
|
||||
// are included.
|
||||
GetAuthorizationUserRoles(ctx context.Context, userID uuid.UUID) (GetAuthorizationUserRolesRow, error)
|
||||
GetBoundaryLogByID(ctx context.Context, id uuid.UUID) (BoundaryLog, error)
|
||||
GetBoundarySessionByID(ctx context.Context, id uuid.UUID) (BoundarySession, error)
|
||||
GetChatACLByID(ctx context.Context, id uuid.UUID) (GetChatACLByIDRow, error)
|
||||
// GetChatAdvisorConfig returns the deployment-wide runtime configuration
|
||||
// for the experimental chat advisor as a JSON blob. Callers unmarshal the
|
||||
@@ -915,6 +920,8 @@ type sqlcQuerier interface {
|
||||
// every member of the org.
|
||||
InsertAllUsersGroup(ctx context.Context, organizationID uuid.UUID) (Group, error)
|
||||
InsertAuditLog(ctx context.Context, arg InsertAuditLogParams) (AuditLog, error)
|
||||
InsertBoundaryLog(ctx context.Context, arg InsertBoundaryLogParams) (BoundaryLog, error)
|
||||
InsertBoundarySession(ctx context.Context, arg InsertBoundarySessionParams) (BoundarySession, error)
|
||||
InsertChat(ctx context.Context, arg InsertChatParams) (Chat, error)
|
||||
// updated_at is the retention clock used by DeleteOldChatDebugRuns.
|
||||
// Set it on every write to keep retention semantics correct.
|
||||
@@ -1039,6 +1046,10 @@ type sqlcQuerier interface {
|
||||
ListAIBridgeTokenUsagesByInterceptionIDs(ctx context.Context, interceptionIds []uuid.UUID) ([]AIBridgeTokenUsage, error)
|
||||
ListAIBridgeToolUsagesByInterceptionIDs(ctx context.Context, interceptionIds []uuid.UUID) ([]AIBridgeToolUsage, error)
|
||||
ListAIBridgeUserPromptsByInterceptionIDs(ctx context.Context, interceptionIds []uuid.UUID) ([]AIBridgeUserPrompt, error)
|
||||
// Lists boundary logs for a session, sorted by sequence number ascending.
|
||||
// Supports optional exclusive sequence number bounds (seq_after, seq_before)
|
||||
// for fetching events between two known interceptions.
|
||||
ListBoundaryLogsBySessionID(ctx context.Context, arg ListBoundaryLogsBySessionIDParams) ([]BoundaryLog, error)
|
||||
ListChatUsageLimitGroupOverrides(ctx context.Context) ([]ListChatUsageLimitGroupOverridesRow, error)
|
||||
ListChatUsageLimitOverrides(ctx context.Context) ([]ListChatUsageLimitOverridesRow, error)
|
||||
ListProvisionerKeysByOrganization(ctx context.Context, organizationID uuid.UUID) ([]ProvisionerKey, error)
|
||||
|
||||
@@ -3549,6 +3549,243 @@ func (q *sqlQuerier) InsertAuditLog(ctx context.Context, arg InsertAuditLogParam
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteOldBoundaryLogs = `-- name: DeleteOldBoundaryLogs :execrows
|
||||
WITH old_logs AS (
|
||||
SELECT id
|
||||
FROM boundary_logs
|
||||
WHERE captured_at < $1::timestamptz
|
||||
ORDER BY captured_at ASC
|
||||
LIMIT $2
|
||||
)
|
||||
DELETE FROM boundary_logs
|
||||
USING old_logs
|
||||
WHERE boundary_logs.id = old_logs.id
|
||||
`
|
||||
|
||||
type DeleteOldBoundaryLogsParams struct {
|
||||
BeforeTime time.Time `db:"before_time" json:"before_time"`
|
||||
LimitCount int32 `db:"limit_count" json:"limit_count"`
|
||||
}
|
||||
|
||||
// Deletes boundary logs older than the given time, bounded by a row limit
|
||||
// to avoid long-running transactions.
|
||||
func (q *sqlQuerier) DeleteOldBoundaryLogs(ctx context.Context, arg DeleteOldBoundaryLogsParams) (int64, error) {
|
||||
result, err := q.db.ExecContext(ctx, deleteOldBoundaryLogs, arg.BeforeTime, arg.LimitCount)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected()
|
||||
}
|
||||
|
||||
const getBoundaryLogByID = `-- name: GetBoundaryLogByID :one
|
||||
SELECT id, session_id, sequence_number, captured_at, created_at, proto, method, detail, matched_rule FROM boundary_logs WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetBoundaryLogByID(ctx context.Context, id uuid.UUID) (BoundaryLog, error) {
|
||||
row := q.db.QueryRowContext(ctx, getBoundaryLogByID, id)
|
||||
var i BoundaryLog
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.SessionID,
|
||||
&i.SequenceNumber,
|
||||
&i.CapturedAt,
|
||||
&i.CreatedAt,
|
||||
&i.Proto,
|
||||
&i.Method,
|
||||
&i.Detail,
|
||||
&i.MatchedRule,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getBoundarySessionByID = `-- name: GetBoundarySessionByID :one
|
||||
SELECT id, workspace_agent_id, confined_process_name, started_at, updated_at FROM boundary_sessions WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetBoundarySessionByID(ctx context.Context, id uuid.UUID) (BoundarySession, error) {
|
||||
row := q.db.QueryRowContext(ctx, getBoundarySessionByID, id)
|
||||
var i BoundarySession
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.WorkspaceAgentID,
|
||||
&i.ConfinedProcessName,
|
||||
&i.StartedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const insertBoundaryLog = `-- name: InsertBoundaryLog :one
|
||||
INSERT INTO boundary_logs (
|
||||
id,
|
||||
session_id,
|
||||
sequence_number,
|
||||
captured_at,
|
||||
created_at,
|
||||
proto,
|
||||
method,
|
||||
detail,
|
||||
matched_rule
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8,
|
||||
$9
|
||||
) RETURNING id, session_id, sequence_number, captured_at, created_at, proto, method, detail, matched_rule
|
||||
`
|
||||
|
||||
type InsertBoundaryLogParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
SessionID uuid.UUID `db:"session_id" json:"session_id"`
|
||||
SequenceNumber int32 `db:"sequence_number" json:"sequence_number"`
|
||||
CapturedAt time.Time `db:"captured_at" json:"captured_at"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
Proto string `db:"proto" json:"proto"`
|
||||
Method string `db:"method" json:"method"`
|
||||
Detail string `db:"detail" json:"detail"`
|
||||
MatchedRule sql.NullString `db:"matched_rule" json:"matched_rule"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertBoundaryLog(ctx context.Context, arg InsertBoundaryLogParams) (BoundaryLog, error) {
|
||||
row := q.db.QueryRowContext(ctx, insertBoundaryLog,
|
||||
arg.ID,
|
||||
arg.SessionID,
|
||||
arg.SequenceNumber,
|
||||
arg.CapturedAt,
|
||||
arg.CreatedAt,
|
||||
arg.Proto,
|
||||
arg.Method,
|
||||
arg.Detail,
|
||||
arg.MatchedRule,
|
||||
)
|
||||
var i BoundaryLog
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.SessionID,
|
||||
&i.SequenceNumber,
|
||||
&i.CapturedAt,
|
||||
&i.CreatedAt,
|
||||
&i.Proto,
|
||||
&i.Method,
|
||||
&i.Detail,
|
||||
&i.MatchedRule,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const insertBoundarySession = `-- name: InsertBoundarySession :one
|
||||
INSERT INTO boundary_sessions (
|
||||
id,
|
||||
workspace_agent_id,
|
||||
confined_process_name,
|
||||
started_at,
|
||||
updated_at
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5
|
||||
) RETURNING id, workspace_agent_id, confined_process_name, started_at, updated_at
|
||||
`
|
||||
|
||||
type InsertBoundarySessionParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"`
|
||||
ConfinedProcessName string `db:"confined_process_name" json:"confined_process_name"`
|
||||
StartedAt time.Time `db:"started_at" json:"started_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertBoundarySession(ctx context.Context, arg InsertBoundarySessionParams) (BoundarySession, error) {
|
||||
row := q.db.QueryRowContext(ctx, insertBoundarySession,
|
||||
arg.ID,
|
||||
arg.WorkspaceAgentID,
|
||||
arg.ConfinedProcessName,
|
||||
arg.StartedAt,
|
||||
arg.UpdatedAt,
|
||||
)
|
||||
var i BoundarySession
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.WorkspaceAgentID,
|
||||
&i.ConfinedProcessName,
|
||||
&i.StartedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listBoundaryLogsBySessionID = `-- name: ListBoundaryLogsBySessionID :many
|
||||
SELECT id, session_id, sequence_number, captured_at, created_at, proto, method, detail, matched_rule
|
||||
FROM boundary_logs
|
||||
WHERE
|
||||
session_id = $1
|
||||
AND CASE
|
||||
WHEN $2::int IS NOT NULL THEN sequence_number > $2
|
||||
ELSE true
|
||||
END
|
||||
AND CASE
|
||||
WHEN $3::int IS NOT NULL THEN sequence_number < $3
|
||||
ELSE true
|
||||
END
|
||||
ORDER BY sequence_number ASC
|
||||
LIMIT COALESCE(NULLIF($4::int, 0), 100)
|
||||
`
|
||||
|
||||
type ListBoundaryLogsBySessionIDParams struct {
|
||||
SessionID uuid.UUID `db:"session_id" json:"session_id"`
|
||||
SeqAfter sql.NullInt32 `db:"seq_after" json:"seq_after"`
|
||||
SeqBefore sql.NullInt32 `db:"seq_before" json:"seq_before"`
|
||||
LimitOpt int32 `db:"limit_opt" json:"limit_opt"`
|
||||
}
|
||||
|
||||
// Lists boundary logs for a session, sorted by sequence number ascending.
|
||||
// Supports optional exclusive sequence number bounds (seq_after, seq_before)
|
||||
// for fetching events between two known interceptions.
|
||||
func (q *sqlQuerier) ListBoundaryLogsBySessionID(ctx context.Context, arg ListBoundaryLogsBySessionIDParams) ([]BoundaryLog, error) {
|
||||
rows, err := q.db.QueryContext(ctx, listBoundaryLogsBySessionID,
|
||||
arg.SessionID,
|
||||
arg.SeqAfter,
|
||||
arg.SeqBefore,
|
||||
arg.LimitOpt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []BoundaryLog
|
||||
for rows.Next() {
|
||||
var i BoundaryLog
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.SessionID,
|
||||
&i.SequenceNumber,
|
||||
&i.CapturedAt,
|
||||
&i.CreatedAt,
|
||||
&i.Proto,
|
||||
&i.Method,
|
||||
&i.Detail,
|
||||
&i.MatchedRule,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getAndResetBoundaryUsageSummary = `-- name: GetAndResetBoundaryUsageSummary :one
|
||||
WITH deleted AS (
|
||||
DELETE FROM boundary_usage_stats
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
-- name: InsertBoundarySession :one
|
||||
INSERT INTO boundary_sessions (
|
||||
id,
|
||||
workspace_agent_id,
|
||||
confined_process_name,
|
||||
started_at,
|
||||
updated_at
|
||||
) VALUES (
|
||||
@id,
|
||||
@workspace_agent_id,
|
||||
@confined_process_name,
|
||||
@started_at,
|
||||
@updated_at
|
||||
) RETURNING *;
|
||||
|
||||
-- name: GetBoundarySessionByID :one
|
||||
SELECT * FROM boundary_sessions WHERE id = @id;
|
||||
|
||||
-- name: InsertBoundaryLog :one
|
||||
INSERT INTO boundary_logs (
|
||||
id,
|
||||
session_id,
|
||||
sequence_number,
|
||||
captured_at,
|
||||
created_at,
|
||||
proto,
|
||||
method,
|
||||
detail,
|
||||
matched_rule
|
||||
) VALUES (
|
||||
@id,
|
||||
@session_id,
|
||||
@sequence_number,
|
||||
@captured_at,
|
||||
@created_at,
|
||||
@proto,
|
||||
@method,
|
||||
@detail,
|
||||
@matched_rule
|
||||
) RETURNING *;
|
||||
|
||||
-- name: GetBoundaryLogByID :one
|
||||
SELECT * FROM boundary_logs WHERE id = @id;
|
||||
|
||||
-- name: ListBoundaryLogsBySessionID :many
|
||||
-- Lists boundary logs for a session, sorted by sequence number ascending.
|
||||
-- Supports optional exclusive sequence number bounds (seq_after, seq_before)
|
||||
-- for fetching events between two known interceptions.
|
||||
SELECT *
|
||||
FROM boundary_logs
|
||||
WHERE
|
||||
session_id = @session_id
|
||||
AND CASE
|
||||
WHEN sqlc.narg('seq_after')::int IS NOT NULL THEN sequence_number > sqlc.narg('seq_after')
|
||||
ELSE true
|
||||
END
|
||||
AND CASE
|
||||
WHEN sqlc.narg('seq_before')::int IS NOT NULL THEN sequence_number < sqlc.narg('seq_before')
|
||||
ELSE true
|
||||
END
|
||||
ORDER BY sequence_number ASC
|
||||
LIMIT COALESCE(NULLIF(@limit_opt::int, 0), 100);
|
||||
|
||||
-- name: DeleteOldBoundaryLogs :execrows
|
||||
-- Deletes boundary logs older than the given time, bounded by a row limit
|
||||
-- to avoid long-running transactions.
|
||||
WITH old_logs AS (
|
||||
SELECT id
|
||||
FROM boundary_logs
|
||||
WHERE captured_at < @before_time::timestamptz
|
||||
ORDER BY captured_at ASC
|
||||
LIMIT @limit_count
|
||||
)
|
||||
DELETE FROM boundary_logs
|
||||
USING old_logs
|
||||
WHERE boundary_logs.id = old_logs.id;
|
||||
@@ -17,6 +17,8 @@ const (
|
||||
UniqueAibridgeUserPromptsPkey UniqueConstraint = "aibridge_user_prompts_pkey" // ALTER TABLE ONLY aibridge_user_prompts ADD CONSTRAINT aibridge_user_prompts_pkey PRIMARY KEY (id);
|
||||
UniqueAPIKeysPkey UniqueConstraint = "api_keys_pkey" // ALTER TABLE ONLY api_keys ADD CONSTRAINT api_keys_pkey PRIMARY KEY (id);
|
||||
UniqueAuditLogsPkey UniqueConstraint = "audit_logs_pkey" // ALTER TABLE ONLY audit_logs ADD CONSTRAINT audit_logs_pkey PRIMARY KEY (id);
|
||||
UniqueBoundaryLogsPkey UniqueConstraint = "boundary_logs_pkey" // ALTER TABLE ONLY boundary_logs ADD CONSTRAINT boundary_logs_pkey PRIMARY KEY (id);
|
||||
UniqueBoundarySessionsPkey UniqueConstraint = "boundary_sessions_pkey" // ALTER TABLE ONLY boundary_sessions ADD CONSTRAINT boundary_sessions_pkey PRIMARY KEY (id);
|
||||
UniqueBoundaryUsageStatsPkey UniqueConstraint = "boundary_usage_stats_pkey" // ALTER TABLE ONLY boundary_usage_stats ADD CONSTRAINT boundary_usage_stats_pkey PRIMARY KEY (replica_id);
|
||||
UniqueChatDebugRunsPkey UniqueConstraint = "chat_debug_runs_pkey" // ALTER TABLE ONLY chat_debug_runs ADD CONSTRAINT chat_debug_runs_pkey PRIMARY KEY (id);
|
||||
UniqueChatDebugStepsPkey UniqueConstraint = "chat_debug_steps_pkey" // ALTER TABLE ONLY chat_debug_steps ADD CONSTRAINT chat_debug_steps_pkey PRIMARY KEY (id);
|
||||
|
||||
Reference in New Issue
Block a user