Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b4b82d6
chore(deps): bump tar from 7.4.4 to 7.5.1 in the npm-minor group (#4199)
dependabot[bot] Sep 24, 2025
34aa1da
chore(deps): bump supabase/postgres from 17.6.1.005 to 17.6.1.007 in …
dependabot[bot] Sep 24, 2025
2b324ad
feat: mcp url
gregnr Sep 20, 2025
bfa9bbc
chore: update studio version
gregnr Sep 23, 2025
07f2f2d
fix(mcp): bump studio version
gregnr Sep 23, 2025
04fb522
feat: mcp url (#4186)
gregnr Sep 24, 2025
f53d795
docs: update branch names in contributing guide
gregnr Sep 24, 2025
0ad8340
docs: update branch names in contributing guide (#4203)
sweatybridge Sep 24, 2025
f5e1774
fix: replace auth header for backwards compatibility
sweatybridge Sep 25, 2025
d81798f
fix: replace auth header for backwards compatibility (#4206)
sweatybridge Sep 25, 2025
8d577b7
feat(auth): add OAuth server configuration support (#4207)
cemalkilic Sep 25, 2025
c732244
chore(deps): bump supabase/storage-api
dependabot[bot] Sep 25, 2025
ab4b50a
chore(deps): bump supabase/storage-api from v1.27.4 to v1.27.6 in /pk…
sweatybridge Sep 25, 2025
c4bb5b7
fix: Update signed upload url default expiration to match prod
itslenny Sep 18, 2025
7c9df87
revert #4172 which set the env in the wrong container
itslenny Sep 25, 2025
4f854bd
chore(deps): bump supabase/postgres from 17.6.1.007 to 17.6.1.010 in …
dependabot[bot] Sep 26, 2025
889c321
chore: sync API types from infrastructure (#4209)
kiwicopple Sep 26, 2025
05a2f39
fix: Update signed upload url default expiration to match prod (#4180)
sweatybridge Sep 26, 2025
409851b
fix: ignore closed pipe error debug flag
sweatybridge Sep 26, 2025
a64266b
fix: ignore closed pipe error debug flag (#4213)
sweatybridge Sep 28, 2025
5f6048a
chore: sync API types from infrastructure (#4217)
kiwicopple Sep 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

## Release process

We release to stable channel every two weeks.
We release to stable channel every two weeks via `main` branch.

We release to beta channel on merge to `main` branch.
We release to beta channel on merge to `develop` branch.

Hotfixes are released manually. Follow these steps:

Expand Down
1 change: 0 additions & 1 deletion internal/db/start/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,6 @@ func initStorageJob(host string) utils.DockerJob {
// TODO: https://github.com/supabase/storage-api/issues/55
"REGION=stub",
"GLOBAL_S3_BUCKET=stub",
"SIGNED_UPLOAD_URL_EXPIRATION_TIME=7200",
},
Cmd: []string{"node", "dist/scripts/migrate-call.js"},
}
Expand Down
8 changes: 6 additions & 2 deletions internal/debug/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,12 @@ func (p *Proxy) DialFunc(ctx context.Context, network, addr string) (net.Conn, e
go frontend.forward(backend, p.errChan)

for {
// Since pgx closes connection first, every EOF is seen as unexpected
if err := <-p.errChan; err != nil && !errors.Is(err, io.ErrUnexpectedEOF) {
if err := <-p.errChan; err != nil &&
// Since pgx closes connection first, every EOF is seen as unexpected
!errors.Is(err, io.ErrUnexpectedEOF) &&
// Frontend might receive a reply after pgx closes the connection, in
// which case the backend will write to a closed pipe. So ignore.
!errors.Is(err, io.ErrClosedPipe) {
panic(err)
}
}
Expand Down
23 changes: 20 additions & 3 deletions internal/start/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ type kongConfig struct {
RestId string
RealtimeId string
StorageId string
StudioId string
PgmetaId string
EdgeRuntimeId string
LogflareId string
Expand Down Expand Up @@ -340,16 +341,22 @@ EOF
RestId: utils.RestId,
RealtimeId: utils.Config.Realtime.TenantId,
StorageId: utils.StorageId,
StudioId: utils.StudioId,
PgmetaId: utils.PgmetaId,
EdgeRuntimeId: utils.EdgeRuntimeId,
LogflareId: utils.LogflareId,
PoolerId: utils.PoolerId,
ApiHost: utils.Config.Hostname,
ApiPort: utils.Config.Api.Port,
BearerToken: fmt.Sprintf(
// Pass down apikey as Authorization header for backwards compatibility with legacy JWT.
// If Authorization header is already set, Kong simply skips evaluating this Lua script.
`$((function() return (headers.apikey == '%s' and 'Bearer %s') or (headers.apikey == '%s' and 'Bearer %s') or headers.apikey end)())`,
// If Authorization header is set to a self-minted JWT, we want to pass it down.
// Legacy supabase-js may set Authorization header to Bearer <apikey>. We must remove it
// to avoid failing JWT validation.
// If Authorization header is missing, we want to match against apikey header to set the
// default JWT for downstream services.
// Finally, the apikey header may be set to a legacy JWT. In that case, we want to copy
// it to Authorization header for backwards compatibility.
`$((function() return (headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or (headers.apikey == '%s' and 'Bearer %s') or (headers.apikey == '%s' and 'Bearer %s') or headers.apikey end)())`,
utils.Config.Auth.SecretKey.Value,
utils.Config.Auth.ServiceRoleKey.Value,
utils.Config.Auth.PublishableKey.Value,
Expand Down Expand Up @@ -692,6 +699,15 @@ EOF
}
env = append(env, fmt.Sprintf("GOTRUE_EXTERNAL_WEB3_SOLANA_ENABLED=%v", utils.Config.Auth.Web3.Solana.Enabled))

// OAuth server configuration
if utils.Config.Auth.OAuthServer.Enabled {
env = append(env,
fmt.Sprintf("GOTRUE_OAUTH_SERVER_ENABLED=%v", utils.Config.Auth.OAuthServer.Enabled),
"GOTRUE_OAUTH_SERVER_AUTHORIZATION_PATH="+utils.Config.Auth.OAuthServer.AuthorizationUrlPath,
fmt.Sprintf("GOTRUE_OAUTH_SERVER_ALLOW_DYNAMIC_REGISTRATION=%v", utils.Config.Auth.OAuthServer.AllowDynamicRegistration),
)
}

if _, err := utils.DockerStart(
ctx,
container.Config{
Expand Down Expand Up @@ -883,6 +899,7 @@ EOF
"S3_PROTOCOL_PREFIX=/storage/v1",
"UPLOAD_FILE_SIZE_LIMIT=52428800000",
"UPLOAD_FILE_SIZE_LIMIT_STANDARD=5242880000",
"SIGNED_UPLOAD_URL_EXPIRATION_TIME=7200",
},
Healthcheck: &container.HealthConfig{
// For some reason, localhost resolves to IPv6 address on GitPod which breaks healthcheck.
Expand Down
38 changes: 25 additions & 13 deletions internal/start/templates/kong.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ services:
- name: cors
- name: request-transformer
config:
add:
replace:
headers:
- "Authorization: {{ .BearerToken }}"
- name: auth-v1-open-callback
Expand All @@ -27,7 +27,7 @@ services:
- name: cors
- name: request-transformer
config:
add:
replace:
headers:
- "Authorization: {{ .BearerToken }}"
- name: auth-v1-open-authorize
Expand All @@ -42,7 +42,7 @@ services:
- name: cors
- name: request-transformer
config:
add:
replace:
headers:
- "Authorization: {{ .BearerToken }}"
- name: auth-v1
Expand All @@ -57,7 +57,7 @@ services:
- name: cors
- name: request-transformer
config:
add:
replace:
headers:
- "Authorization: {{ .BearerToken }}"
- name: rest-v1
Expand All @@ -72,7 +72,7 @@ services:
- name: cors
- name: request-transformer
config:
add:
replace:
headers:
- "Authorization: {{ .BearerToken }}"
- name: rest-admin-v1
Expand All @@ -87,7 +87,7 @@ services:
- name: cors
- name: request-transformer
config:
add:
replace:
headers:
- "Authorization: {{ .BearerToken }}"
- name: graphql-v1
Expand All @@ -105,6 +105,8 @@ services:
add:
headers:
- "Content-Profile: graphql_public"
replace:
headers:
- "Authorization: {{ .BearerToken }}"
- name: realtime-v1-ws
_comment: "Realtime: /realtime/v1/* -> ws://realtime:4000/socket/websocket"
Expand All @@ -119,7 +121,7 @@ services:
- name: cors
- name: request-transformer
config:
add:
replace:
headers:
- "Authorization: {{ .BearerToken }}"
- name: realtime-v1-longpoll
Expand All @@ -135,7 +137,7 @@ services:
- name: cors
- name: request-transformer
config:
add:
replace:
headers:
- "Authorization: {{ .BearerToken }}"
- name: realtime-v1-rest
Expand All @@ -151,7 +153,7 @@ services:
- name: cors
- name: request-transformer
config:
add:
replace:
headers:
- "Authorization: {{ .BearerToken }}"
- name: storage-v1
Expand All @@ -166,7 +168,7 @@ services:
- name: cors
- name: request-transformer
config:
add:
replace:
headers:
- "Authorization: {{ .BearerToken }}"
- name: pg-meta
Expand All @@ -192,7 +194,7 @@ services:
- name: cors
- name: request-transformer
config:
add:
replace:
headers:
- "Authorization: {{ .BearerToken }}"
- name: analytics-v1
Expand All @@ -207,7 +209,7 @@ services:
- name: cors
- name: request-transformer
config:
add:
replace:
headers:
- "Authorization: {{ .BearerToken }}"
- name: pooler-v2-ws
Expand All @@ -223,6 +225,16 @@ services:
- name: cors
- name: request-transformer
config:
add:
replace:
headers:
- "Authorization: {{ .BearerToken }}"
- name: mcp
_comment: "MCP: /mcp -> http://studio:3000/api/mcp"
url: http://{{ .StudioId }}:3000/api/mcp
routes:
- name: mcp
strip_path: true
paths:
- /mcp
plugins:
- name: cors
22 changes: 17 additions & 5 deletions internal/status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type CustomName struct {
ApiURL string `env:"api.url,default=API_URL"`
GraphqlURL string `env:"api.graphql_url,default=GRAPHQL_URL"`
StorageS3URL string `env:"api.storage_s3_url,default=STORAGE_S3_URL"`
McpURL string `env:"api.mcp_url,default=MCP_URL"`
DbURL string `env:"db.url,default=DB_URL"`
StudioURL string `env:"studio.url,default=STUDIO_URL"`
InbucketURL string `env:"inbucket.url,default=INBUCKET_URL,deprecated"`
Expand All @@ -46,25 +47,35 @@ func (c *CustomName) toValues(exclude ...string) map[string]string {
values := map[string]string{
c.DbURL: fmt.Sprintf("postgresql://%s@%s:%d/postgres", url.UserPassword("postgres", utils.Config.Db.Password), utils.Config.Hostname, utils.Config.Db.Port),
}
if utils.Config.Api.Enabled && !utils.SliceContains(exclude, utils.RestId) && !utils.SliceContains(exclude, utils.ShortContainerImageName(utils.Config.Api.Image)) {

apiEnabled := utils.Config.Api.Enabled && !utils.SliceContains(exclude, utils.RestId) && !utils.SliceContains(exclude, utils.ShortContainerImageName(utils.Config.Api.Image))
studioEnabled := utils.Config.Studio.Enabled && !utils.SliceContains(exclude, utils.StudioId) && !utils.SliceContains(exclude, utils.ShortContainerImageName(utils.Config.Studio.Image))
authEnabled := utils.Config.Auth.Enabled && !utils.SliceContains(exclude, utils.GotrueId) && !utils.SliceContains(exclude, utils.ShortContainerImageName(utils.Config.Auth.Image))
inbucketEnabled := utils.Config.Inbucket.Enabled && !utils.SliceContains(exclude, utils.InbucketId) && !utils.SliceContains(exclude, utils.ShortContainerImageName(utils.Config.Inbucket.Image))
storageEnabled := utils.Config.Storage.Enabled && !utils.SliceContains(exclude, utils.StorageId) && !utils.SliceContains(exclude, utils.ShortContainerImageName(utils.Config.Storage.Image))

if apiEnabled {
values[c.ApiURL] = utils.Config.Api.ExternalUrl
values[c.GraphqlURL] = utils.GetApiUrl("/graphql/v1")
if studioEnabled {
values[c.McpURL] = utils.GetApiUrl("/mcp")
}
}
if utils.Config.Studio.Enabled && !utils.SliceContains(exclude, utils.StudioId) && !utils.SliceContains(exclude, utils.ShortContainerImageName(utils.Config.Studio.Image)) {
if studioEnabled {
values[c.StudioURL] = fmt.Sprintf("http://%s:%d", utils.Config.Hostname, utils.Config.Studio.Port)
}
if utils.Config.Auth.Enabled && !utils.SliceContains(exclude, utils.GotrueId) && !utils.SliceContains(exclude, utils.ShortContainerImageName(utils.Config.Auth.Image)) {
if authEnabled {
values[c.PublishableKey] = utils.Config.Auth.PublishableKey.Value
values[c.SecretKey] = utils.Config.Auth.SecretKey.Value
values[c.JWTSecret] = utils.Config.Auth.JwtSecret.Value
values[c.AnonKey] = utils.Config.Auth.AnonKey.Value
values[c.ServiceRoleKey] = utils.Config.Auth.ServiceRoleKey.Value
}
if utils.Config.Inbucket.Enabled && !utils.SliceContains(exclude, utils.InbucketId) && !utils.SliceContains(exclude, utils.ShortContainerImageName(utils.Config.Inbucket.Image)) {
if inbucketEnabled {
values[c.MailpitURL] = fmt.Sprintf("http://%s:%d", utils.Config.Hostname, utils.Config.Inbucket.Port)
values[c.InbucketURL] = fmt.Sprintf("http://%s:%d", utils.Config.Hostname, utils.Config.Inbucket.Port)
}
if utils.Config.Storage.Enabled && !utils.SliceContains(exclude, utils.StorageId) && !utils.SliceContains(exclude, utils.ShortContainerImageName(utils.Config.Storage.Image)) {
if storageEnabled {
values[c.StorageS3URL] = utils.GetApiUrl("/storage/v1/s3")
values[c.StorageS3AccessKeyId] = utils.Config.Storage.S3Credentials.AccessKeyId
values[c.StorageS3SecretAccessKey] = utils.Config.Storage.S3Credentials.SecretAccessKey
Expand Down Expand Up @@ -202,6 +213,7 @@ func PrettyPrint(w io.Writer, exclude ...string) {
ApiURL: " " + utils.Aqua("API URL"),
GraphqlURL: " " + utils.Aqua("GraphQL URL"),
StorageS3URL: " " + utils.Aqua("S3 Storage URL"),
McpURL: " " + utils.Aqua("MCP URL"),
DbURL: " " + utils.Aqua("Database URL"),
StudioURL: " " + utils.Aqua("Studio URL"),
InbucketURL: " " + utils.Aqua("Inbucket URL"),
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"bin-links": "^5.0.0",
"https-proxy-agent": "^7.0.2",
"node-fetch": "^3.3.2",
"tar": "7.4.4"
"tar": "7.5.1"
},
"release": {
"branches": [
Expand Down
Loading