{
  "repo": "tidbcloud/auditservice",
  "binary": "subscriber",
  "binary_path": "cmd/subscriber/main.go",
  "dependencies": {
    "AuditSubscriberLifecycle.Startup": {
      "infrastructure": {},
      "services": []
    },
    "AuditSubscriberLifecycle.InitializeEventBus": {
      "infrastructure": {},
      "services": []
    },
    "AuditSubscriberLifecycle.InitializeDB": {
      "infrastructure": {
        "filesystem": {
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "READ FILE",
              "trigger": "Load the local config file before initializing downstream service clients",
              "conditions": [
                "Always"
              ],
              "implementation": "configs.GetConfig() -> configor.Load(config, configPath)"
            }
          ]
        },
        "mysql": {
          "conditions": [
            "Only when DB_DSN environment variable is set"
          ],
          "operations": [
            {
              "operation": "OPEN DB HANDLE",
              "trigger": "Create the shared auditlog database handle used by ProcessAuditLog",
              "conditions": [
                "os.Getenv(\"DB_DSN\") != \"\""
              ],
              "implementation": "entity.InitDB(cfg, false) -> gorm.Open(mysql.Open(dsn), &gorm.Config{})"
            }
          ]
        }
      },
      "services": []
    },
    "AuditSubscriberLifecycle.InitializeAuditService": {
      "infrastructure": {},
      "services": [
        {
          "name": "account-svc",
          "target_interface": "gRPC AccountAPIService",
          "operation_type": "grpc_connect",
          "target_repo": "tidbcloud/account",
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "CREATE CLIENT CONN",
              "trigger": "Initialize the account service client used later by event enrichment and impersonation/org membership checks",
              "conditions": [
                "Always"
              ],
              "implementation": "account.NewAccountSvc(ctx, cfg) -> tigrpc.NewClientConn(cfg.AccountSvcAddr, ...)"
            }
          ]
        },
        {
          "name": "core-svc",
          "target_interface": "gRPC CoreService",
          "operation_type": "grpc_connect",
          "target_repo": "tidbcloud/core-svc",
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "CREATE CLIENT CONN",
              "trigger": "Initialize the cluster-management client used later by event enrichment",
              "conditions": [
                "Always"
              ],
              "implementation": "cluster_management.NewCMSvc(ctx, cfg) -> tigrpc.NewClientConn(cfg.CMSvcAddr, ...)"
            }
          ]
        },
        {
          "name": "acl-svc",
          "target_interface": "gRPC AccessService",
          "operation_type": "grpc_connect",
          "target_repo": "tidbcloud/account",
          "conditions": [
            "Always"
          ],
          "operations": [
            {
              "operation": "CREATE CLIENT CONN",
              "trigger": "Initialize the ACL client used later by API key event enrichment",
              "conditions": [
                "Always"
              ],
              "implementation": "acl.NewAclSvcIface(ctx, cfg) -> tigrpc.NewClientConn(cfg.ACLSvcAddr, ...)"
            }
          ]
        }
      ]
    },
    "AuditSubscriberLifecycle.RegisterEventHandler": {
      "infrastructure": {},
      "services": []
    },
    "AuditSubscriberLifecycle.RunEventBus": {
      "infrastructure": {},
      "services": []
    },
    "Runtime AuditSubscriberEvents.ConsumeEventBusEvent": {
      "infrastructure": {
        "mysql": {
          "conditions": [
            "Only after event type/data assertion succeeds and CreateEvent reaches the persistence path"
          ],
          "operations": [
            {
              "operation": "SELECT",
              "trigger": "Check whether the organization has audit logging enabled before persisting the event",
              "conditions": [
                "fullEvent.OrgID is present or zero-value lookup still executes"
              ],
              "implementation": "auditSvc.CreateEvent -> auditDao.QueryOrgSettings(ctx, db, fullEvent.OrgID)"
            },
            {
              "operation": "INSERT",
              "trigger": "Persist internal audit events after validation and org-setting checks pass",
              "conditions": [
                "consts.IsInternalEventType(fullEvent.Type) == true",
                "checkIfEventValid(event, true) == true",
                "org settings do not reject the event"
              ],
              "implementation": "auditSvc.CreateEvent -> auditDao.CreateInternalEvent(ctx, db, dto.ConvertToDBInternalEvent(fullEvent))"
            },
            {
              "operation": "INSERT",
              "trigger": "Persist external audit events after validation and org-setting checks pass",
              "conditions": [
                "consts.IsInternalEventType(fullEvent.Type) == false",
                "checkIfEventValid(event, false) == true",
                "org settings do not reject the event"
              ],
              "implementation": "auditSvc.CreateEvent -> auditDao.CreateEvent(ctx, db, dto.ConvertToDBEvent(fullEvent))"
            }
          ]
        }
      },
      "services": [
        {
          "name": "account-svc",
          "target_interface": "GetUserByToken / GetIdentityByAccessToken / GetOrgByID / GetProjectByID / GetAuthUser / ListOrgsByUser",
          "operation_type": "grpc",
          "target_repo": "tidbcloud/account",
          "conditions": [
            "Only on the matching enrichment or validation branches"
          ],
          "operations": [
            {
              "operation": "GET USER BY TOKEN",
              "trigger": "Resolve user identity from a user token",
              "conditions": [
                "event.UserToken != \"\""
              ],
              "implementation": "auditSvc.extendEvent -> accountSvc.GetUserByToken(ctx, event.UserToken)"
            },
            {
              "operation": "GET IDENTITY BY ACCESS TOKEN",
              "trigger": "Resolve access-token identity fields",
              "conditions": [
                "event.AccessToken != \"\""
              ],
              "implementation": "auditSvc.extendEvent -> accountSvc.GetIdentityByAccessToken(ctx, event.AccessToken)"
            },
            {
              "operation": "GET ORG BY ID",
              "trigger": "Backfill org name when OrgID is known but OrgName is empty",
              "conditions": [
                "event.OrgID != 0",
                "event.OrgName == \"\""
              ],
              "implementation": "auditSvc.extendEvent -> accountSvc.GetOrgByID(ctx, event.OrgID)"
            },
            {
              "operation": "GET PROJECT BY ID",
              "trigger": "Backfill project name when ProjectID is known but ProjectName is empty",
              "conditions": [
                "event.ProjectID != 0",
                "event.ProjectName == \"\""
              ],
              "implementation": "auditSvc.extendEvent -> accountSvc.GetProjectByID(ctx, event.ProjectID)"
            },
            {
              "operation": "GET AUTH USER",
              "trigger": "Reject impersonated users before persisting user-originated events",
              "conditions": [
                "fullEvent.OperatorType == auditlog.OperatorTypeUser"
              ],
              "implementation": "auditSvc.CreateEvent -> accountSvc.GetAuthUser(ctx, fullEvent.OperatorEmail)"
            },
            {
              "operation": "LIST ORGS BY USER",
              "trigger": "Ensure the user belongs to the org before persisting user-originated org-scoped events",
              "conditions": [
                "fullEvent.OperatorType == auditlog.OperatorTypeUser",
                "fullEvent.OrgID != 0"
              ],
              "implementation": "auditSvc.checkUserInOrg -> accountSvc.ListOrgsByUser(ctx, fullEvent.OperatorEmail)"
            }
          ]
        },
        {
          "name": "acl-svc",
          "target_interface": "GetOpenAPIKeyByToken",
          "operation_type": "grpc",
          "target_repo": "tidbcloud/account",
          "conditions": [
            "event.APIPubKey != \"\""
          ],
          "operations": [
            {
              "operation": "GET OPENAPI KEY BY TOKEN",
              "trigger": "Resolve API key creator identity from a public key token",
              "conditions": [
                "event.APIPubKey != \"\""
              ],
              "implementation": "auditSvc.extendEvent -> aclSvc.GetOpenAPIKeyByToken(ctx, event.APIPubKey)"
            }
          ]
        },
        {
          "name": "core-svc",
          "target_interface": "ListClusters",
          "operation_type": "grpc",
          "target_repo": "tidbcloud/core-svc",
          "conditions": [
            "event.ClusterID != 0 and cluster name/type need enrichment"
          ],
          "operations": [
            {
              "operation": "LIST CLUSTERS",
              "trigger": "Backfill cluster name and cluster type from cluster management",
              "conditions": [
                "event.ClusterID != 0",
                "event.ClusterName == \"\" or event.ClusterType == auditlog.ClusterTypeUnknown"
              ],
              "implementation": "auditSvc.extendEvent -> cmSvc.GetClusterByID(ctx, event.ClusterID) -> core.CoreServiceClient.ListClusters(...)"
            }
          ]
        }
      ]
    }
  },
  "notes": [
    "Health endpoints are intentionally omitted from the subscriber runtime surface because they are started only after the blocking eventbus.Run() call returns.",
    "The event consumer persists nothing until extendEvent, org-setting checks, and event validation all pass."
  ]
}
