This document provides an introduction on building a managed service for operation on Sourcegraph Managed Services Platform (MSP).

If you haven’t yet, refer to Use cases to gain a high-level understanding of what MSP offers and what you get when you build a managed service with MSP! You can also refer to MSP technical details for how things work under the hood.

<aside> πŸ‘‹ If you need help at any point, please reach out to #discuss-core-services!

</aside>



MSP runtime

The Core Services team recommends building your service in Go to leverage the service initialization and runtime boilerplate provided by the standalone github.com/sourcegraph/sourcegraph/lib/managedservicesplatform module.

The runtime.Start function outlines the expected "contract" the MSP runtime expects services to fulfil, and ensures your service is compatible with MSP infrastructure:

package main

import (
  "github.com/sourcegraph/sourcegraph/lib/managedservicesplatform/runtime"

  // Your implementation!
  "github.com/sourcegraph/my-service/service"
)

func main() {
  runtime.Start[service.Config](service.Service{})
}

The msp-example service demonstrates and tests a variety of integrations with MSP runtime, and also demonstrates an example in-monorepo Service package structure. The example image is deployed in MSP Testbed infrastructure operations.

Service package structure

In general, you’ll want one of the following package structures for your MSP service:

cmd
 └── my-service
		  β”œβ”€β”€ main.go # runtime.Start(...) here
			β”œβ”€β”€ service/
			β”‚		 β”œβ”€β”€ service.go # service.Service here
			β”‚		 └── config.go  # service.Config here
			└── internal/ # implementation imported by 'service/'
					 └── ...
/
β”œβ”€β”€ cmd/my-service/
β”‚		 └── main.go # runtime.Start(...) here
β”œβ”€β”€ service/
β”‚ 	 β”œβ”€β”€ service.go # service.Service here
β”‚		 └── config.go  # service.Config here
└── internal/ # implementation imported by 'service/'
		 └── ...

At a high level:

  1. Command entrypoint (cmd/my-service) imports your service package and calls runtime.Start

  2. Service entrypoint (service/service.go ) imports the various components that make up your service in internal/... and composes them into a runnable service.

    1. Service configuration should compose and embed sub-package configurations defined by various internal packages. Your internal packages should not import your top-level service configuration; only use what you need, to avoid configuration sprawl. Embedding sub-package configurations also provides a clearer indicator of what options are used by what components. For example:

      // File: service/config.go
      package service
      
      import "my-repo/internal/cloudflare" // internal package
      
      type Config struct {
      	// Just an example: embed subpackage configuration
      	cloudflare.TurnstileConfig
      }
      
      // ...
      
  3. internal/... houses the implementation details behind your service (your API handlers, databases clients, and so on).

Using provisioned resources

In your implementation of runtime.Service, the primary entrypoint Initialize provides a runtime.Contract that is pre-configured with MSP defaults and offers helpers to integrating with MSP-provisioned resources like Cloud Run, Cloud SQL databases, Redis, BigQuery, and more. For example: