Files
PaperClipAI/server/src/services/index.ts
T
Claude (Paperclip)andPaperclip d31c90f293 feat(subscriptions): add BYO subscription credential storage and capture APIs
Add per-user encrypted credential storage for BYO Claude/Codex seats
(PAPA-994). This is the foundation layer for the subscription worker
feature — downstream runtime injection and UX work build on top of it.

Changes:
- packages/db: new `user_subscription_credentials` schema, drizzle
  export, migration 0111 (company+user+provider unique index, cascade
  deletes, encrypted material jsonb column)
- packages/shared: SUBSCRIPTION_CREDENTIAL_* constants (providers,
  kinds, statuses), API path constant, zod validators
  (upsertSubscriptionCredentialSchema with provider/kind cross-validation
  and JSON-material enforcement), redacted read-model schema, full type
  and validator exports
- server/routes: subscription-credential routes (list, get, upsert PUT,
  test-result POST, delete) with board-user-only authz, company-scoped
  access checks, and redacted audit log entries on every mutation
- server/services: subscriptionCredentialService — upsert with
  local_encrypted provider, redacted metadata computation, delete,
  recordTestResult, and a narrow resolveDecryptedMaterial() surface for
  downstream runtime injection only (SHA-256 integrity check on decrypt)
- tests: route unit tests (authz, redaction, validation) and shared
  validator tests (provider/kind cross-validation, JSON material, max
  length, status values)

Hard constraints upheld:
- Plaintext credential material is never stored, logged, or returned
- Access is isolated per company+user; agents cannot manage user seats
- Encryption reuses the existing local_encrypted secret provider

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-24 14:49:52 -07:00

103 lines
4.7 KiB
TypeScript

export { companyService } from "./companies.js";
export { companyArtifactsService } from "./company-artifacts.js";
export { companySearchService } from "./company-search.js";
export { feedbackService } from "./feedback.js";
export { companySkillService } from "./company-skills.js";
export { agentService, deduplicateAgentName } from "./agents.js";
export { agentInstructionsService, syncInstructionsBundleConfigFromFilePath } from "./agent-instructions.js";
export { assetService } from "./assets.js";
export { documentService, extractLegacyPlanBody } from "./documents.js";
export { documentAnnotationService } from "./document-annotations.js";
export {
ISSUE_CONTINUATION_SUMMARY_DOCUMENT_KEY,
buildContinuationSummaryMarkdown,
getIssueContinuationSummaryDocument,
refreshIssueContinuationSummary,
} from "./issue-continuation-summary.js";
export { projectService } from "./projects.js";
export {
clampIssueListLimit,
ISSUE_LIST_DEFAULT_LIMIT,
ISSUE_LIST_MAX_LIMIT,
issueService,
type IssueFilters,
} from "./issues.js";
export { issueThreadInteractionService } from "./issue-thread-interactions.js";
export { issueTreeControlService } from "./issue-tree-control.js";
export { issueApprovalService } from "./issue-approvals.js";
export { issueReferenceService } from "./issue-references.js";
export { issueRecoveryActionService } from "./issue-recovery-actions.js";
export { taskWatchdogService } from "./task-watchdogs.js";
export {
issueIsInTaskWatchdogSubtree,
resolveTaskWatchdogMutationScope,
taskWatchdogScopeAllowsIssueMutation,
} from "./task-watchdog-scope.js";
export {
createExternalObjectDetectorRegistry,
createExternalObjectResolverRegistry,
externalObjectService,
type ExternalObjectDetector,
type ExternalObjectResolver,
type ExternalObjectResolveResult,
type ExternalObjectResolverSnapshot,
} from "./external-objects.js";
export { goalService } from "./goals.js";
export { activityService, type ActivityFilters } from "./activity.js";
export { approvalService } from "./approvals.js";
export { budgetService } from "./budgets.js";
export { secretService } from "./secrets.js";
export {
subscriptionCredentialService,
type UpsertSubscriptionCredentialInput,
type DecryptedSubscriptionCredential,
} from "./subscription-credentials.js";
export { routineService } from "./routines.js";
export { costService } from "./costs.js";
export { financeService } from "./finance.js";
export { heartbeatService } from "./heartbeat.js";
export {
productivityReviewService,
PRODUCTIVITY_REVIEW_ORIGIN_KIND,
} from "./productivity-review.js";
export { classifyIssueGraphLiveness, type IssueLivenessFinding } from "./recovery/index.js";
export { dashboardService } from "./dashboard.js";
export { sidebarBadgeService } from "./sidebar-badges.js";
export { sidebarPreferenceService } from "./sidebar-preferences.js";
export { resourceMembershipService, type ResourceMembershipPolicyHook } from "./resource-memberships.js";
export { inboxDismissalService } from "./inbox-dismissals.js";
export { accessService } from "./access.js";
export {
backfillPrincipalAccessCompatibility,
ensureHumanRoleDefaultGrants,
insertMissingPrincipalGrants,
type PrincipalAccessCompatibilityBackfillStats,
} from "./principal-access-compatibility.js";
export { authorizationService } from "./authorization.js";
export type {
AuthorizationAction,
AuthorizationActor,
AuthorizationDecision,
AuthorizationResource,
} from "./authorization.js";
export { boardAuthService } from "./board-auth.js";
export { instanceSettingsService } from "./instance-settings.js";
export { bootstrapExecutionPolicyFromEnv } from "./execution-policy-bootstrap.js";
export { cloudUpstreamService, reconcileCloudUpstreamRunsOnStartup } from "./cloud-upstreams.js";
export { companyPortabilityService } from "./company-portability.js";
export { teamsCatalogService } from "./teams-catalog.js";
export { environmentService } from "./environments.js";
export { executionWorkspaceService } from "./execution-workspaces.js";
export { workspaceOperationService } from "./workspace-operations.js";
export { workspaceFileResourceService } from "./workspace-file-resources.js";
export { workProductService } from "./work-products.js";
export { logActivity, type LogActivityInput } from "./activity-log.js";
export { notifyHireApproved, type NotifyHireApprovedInput } from "./hire-hook.js";
export { publishLiveEvent, subscribeCompanyLiveEvents } from "./live-events.js";
export {
reconcileCodexLocalManagedHomesOnStartup,
type CodexAuthReconciliationSummary,
} from "./codex-auth-reconciliation.js";
export { reconcilePersistedRuntimeServicesOnStartup, restartDesiredRuntimeServicesOnStartup } from "./workspace-runtime.js";
export { createStorageServiceFromConfig, getStorageService } from "../storage/index.js";