Skip to content

chore: sync upstream SiaFoundation/indexd - #19

Merged
pcfreak30 merged 54 commits into
masterfrom
sync/upstream
Aug 31, 2026
Merged

chore: sync upstream SiaFoundation/indexd#19
pcfreak30 merged 54 commits into
masterfrom
sync/upstream

Conversation

@pcfreak30

@pcfreak30 pcfreak30 commented Aug 31, 2026

Copy link
Copy Markdown
Member

Merges SiaFoundation/indexd:master into LumeWeb/indexd:master (52 upstream commits through 54410b0f).


Summary

This pull request synchronizes the repository with upstream changes from the SiaFoundation/indexd repository, bringing a substantial set of new features, performance improvements, and bug fixes. The primary additions are a new object blocklist for content moderation and a new sharing key system for granting scoped, read-only access to objects without requiring viewer accounts. The update also includes host management improvements, administrative API enhancements, and various performance optimizations.

Object Blocklist (Content Moderation)

  • New Admin API Endpoints: GET /objects/blocklist, GET/PUT/DELETE /objects/blocklist/{objectkey} for managing a list of blocked objects.
  • Blocked Content: Objects on the blocklist are hidden from all listings (GET /objects, sharing key listings) and cannot be fetched, pinned, or shared. Fetching returns HTTP status 451 Unavailable For Legal Reasons.
  • Key Derivation: Object keys are derived from an object's slabs, so blocking a key covers that specific content for every account, including content that hasn't been pinned yet.
  • Unblocking: Unblocking an object restores access and refreshes its event timestamp so clients that have already paged past it can pick it up again.

Sharing Keys

  • New App API Endpoints: POST/GET/DELETE /sharing, GET/DELETE /sharing/{key}, POST/GET/DELETE /sharing/{key}/objects to manage sharing keys and attach/detach objects.
  • Recipient-Facing Endpoints: GET /shared, GET /shared/objects, GET /shared/objects/{id}, GET /shared/hosts provide read-only access to objects via a sharing key.
  • Scoped Access: Each sharing key is derived from the app's key and a unique nonce, granting access only to explicitly attached objects.
  • Account Tokens: The /shared/hosts endpoint includes tokens for paying for downloads from hosts.

Host Management

  • Host Import API: New POST /hosts endpoint to manually add a host and its network addresses to the indexer for scanning. Validates addresses and formats.
  • Forced Scanning: New POST /hosts/scan?force=true endpoint to force scanning every host, regardless of schedule.
  • Address Validation: Improved host address validation, including proper host:port checking and port range validation.

Performance Improvements

  • Batch Database Queries: Object list and shared object queries now properly batch their database access instead of doing individual queries.
  • Pruning Optimizations: Improved efficiency of slab pruning and expired contract sector cleanup.
  • Object Event Pagination: Improved indexes and query performance for object event pagination.

Bug Fixes

  • App Registration: Fixed a bug where an approved connection request could be reused to register multiple app keys. The request is now consumed after first use.
  • Sector Loss Accounting: Fixed an issue where sectors pinned to contracts past their proof height were incorrectly counted as lost.
  • Timestamp Precision: Fixed object event timestamps to consistently truncate to second precision, ensuring client cursors can always advance.
  • Host Timed-Out RPCs: Hosts that stall until a deadline are now properly demoted based on the implied throughput.

Version Updates

  • Updated Go version to 1.27 in the Dockerfile and CI workflows.

Changelog

  • Added a comprehensive CHANGELOG.md update documenting these changes (versions 0.4.2 through 0.4.4).

n8mgr and others added 30 commits July 22, 2026 12:38
update openapi spec

track sharing key stats

track sharing key stats

review fixes

- sync openapi to the SealedObject list response and key stat fields
- fix migration trigger to maintain sharing_keys.size like init.sql
- drop redundant test comments
Support adding and removing sharing keys and shared objects
The UPDATE paths already truncate to whole seconds; inserts took the
column default and kept microseconds. A client cursor carries
milliseconds, so a row with a sub-millisecond remainder stays strictly
greater than the cursor built from it and is redelivered on every poll,
leaving that client's cursor stuck until something else updates the
object.

Changes the default rather than the column type, so no table rewrite.

Rows already written are not corrected here.
Bumps the dependencies group with 1 update: [go.sia.tech/coreutils](https://github.com/SiaFoundation/coreutils).


Updates `go.sia.tech/coreutils` from 0.23.6-0.20260717131215-dfd89d065259 to 0.24.0
- [Release notes](https://github.com/SiaFoundation/coreutils/releases)
- [Changelog](https://github.com/SiaFoundation/coreutils/blob/master/CHANGELOG.md)
- [Commits](https://github.com/SiaFoundation/coreutils/commits/v0.24.0)

---
updated-dependencies:
- dependency-name: go.sia.tech/coreutils
  dependency-version: 0.24.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
n8mgr and others added 18 commits August 17, 2026 10:26
Bumps the dependencies group with 1 update: [golang.org/x/crypto](https://github.com/golang/crypto).


Updates `golang.org/x/crypto` from 0.54.0 to 0.55.0
- [Commits](golang/crypto@v0.54.0...v0.55.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.55.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps the dependencies group with 1 update: [github.com/klauspost/reedsolomon](https://github.com/klauspost/reedsolomon).


Updates `github.com/klauspost/reedsolomon` from 1.14.1 to 1.14.2
- [Release notes](https://github.com/klauspost/reedsolomon/releases)
- [Commits](klauspost/reedsolomon@v1.14.1...v1.14.2)

---
updated-dependencies:
- dependency-name: github.com/klauspost/reedsolomon
  dependency-version: 1.14.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
# Conflicts:
#	.github/workflows/publish.yml
@kody-ai

kody-ai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

Comment thread api/app/app.go
// register one account.
var status int
var err error
a.mu.Lock()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Bug high

The one-time connection request is removed from a.authRequests at line 904 under the lock before RegisterAppKey succeeds at line 914, consuming the approval even on failure. ErrKeyExhausted (403), ErrKeyNotFound (401), or transient/internal errors permanently discard the user's request with no retry possible; delete the request only after successful registration, or move the delete into the success branch of the switch (lines 921-937).

Prompt for LLM

File api/app/app.go:

Line 892:

The one-time connection request is removed from a.authRequests at line 904 under the lock before RegisterAppKey succeeds at line 914, consuming the approval even on failure. ErrKeyExhausted (403), ErrKeyNotFound (401), or transient/internal errors permanently discard the user's request with no retry possible; delete the request only after successful registration, or move the delete into the success branch of the switch (lines 921-937).

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread api/app/app.go
}

err := a.accounts.RegisterAppKey(authReq.ConnectKey, registerReq.AppKey, accounts.AppMeta{
// validate the request and consume it, so that one approval can only ever

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Bug high

The auth request is deleted from a.authRequests (line 904) under the lock before RegisterAppKey (line 914) succeeds, permanently consuming the one-time approval on transient failures like DB errors, ErrKeyExhausted, or ErrKeyNotFound. Move the deletion into the success path after line 936 to preserve the consume-once anti-replay guarantee while retaining the request on failure.

default:
	// deferred: only consume on successful registration below
}
a.mu.Unlock()
if err != nil {
	jc.Error(err, status)
	return
} else if authReq.UserSecret == (types.Hash256{}) {
	panic("user secret is empty for approved request")
}

err = a.accounts.RegisterAppKey(authReq.ConnectKey, registerReq.AppKey, accounts.AppMeta{
	ID:          authReq.Request.AppID,
	Name:        authReq.Request.Name,
	Description: authReq.Request.Description,
	LogoURL:     authReq.Request.LogoURL,
	ServiceURL:  authReq.Request.ServiceURL,
})
// on success (no error), consume the request under the lock
if err == nil {
	a.mu.Lock()
	delete(a.authRequests, requestID)
	a.mu.Unlock()
}
Prompt for LLM

File api/app/app.go:

Line 888:

The auth request is deleted from a.authRequests (line 904) under the lock before RegisterAppKey (line 914) succeeds, permanently consuming the one-time approval on transient failures like DB errors, ErrKeyExhausted, or ErrKeyNotFound. Move the deletion into the success path after line 936 to preserve the consume-once anti-replay guarantee while retaining the request on failure.

Suggested Code:

	default:
		// deferred: only consume on successful registration below
	}
	a.mu.Unlock()
	if err != nil {
		jc.Error(err, status)
		return
	} else if authReq.UserSecret == (types.Hash256{}) {
		panic("user secret is empty for approved request")
	}

	err = a.accounts.RegisterAppKey(authReq.ConnectKey, registerReq.AppKey, accounts.AppMeta{
		ID:          authReq.Request.AppID,
		Name:        authReq.Request.Name,
		Description: authReq.Request.Description,
		LogoURL:     authReq.Request.LogoURL,
		ServiceURL:  authReq.Request.ServiceURL,
	})
	// on success (no error), consume the request under the lock
	if err == nil {
		a.mu.Lock()
		delete(a.authRequests, requestID)
		a.mu.Unlock()
	}

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment thread api/app/auth.go
// key must resolve to an existing, unexpired sharing key. On success it returns
// that sharing key.
func validateSharedURLAuth(jc jape.Context, hostname string, store Sharing) (sharing.Key, bool) {
pk, ok := ValidateURLSignature(jc.Request, jc.ResponseWriter, hostname)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Kody Rules low

No hard-coded secrets are introduced in this diff; only the provided public key and environment-derived values are used. No issue found.

Kody rule violation: Ban hard-coded secrets in Go source

Prompt for LLM

File api/app/auth.go:

Line 131:

No hard-coded secrets are introduced in this diff; only the provided public key and environment-derived values are used. No issue found.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

return fmt.Errorf("failed to get object: %w", err)
}

// capture the object's sizes at attach time so the trigger can

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Bug medium

AddSharedObject persists a one-time snapshot of size/pinned_data/pinned_size into shared_objects without updating it when the underlying object's slab footprint changes, leaving both the shared_objects row and the key's aggregate totals permanently stale. Recompute and refresh the shared_objects size columns on object update, or recalculate the key totals on read rather than relying on the maintain-totals trigger alone.

Prompt for LLM

File persist/postgres/sharing.go:

Line 189:

AddSharedObject persists a one-time snapshot of size/pinned_data/pinned_size into shared_objects without updating it when the underlying object's slab footprint changes, leaving both the shared_objects row and the key's aggregate totals permanently stale. Recompute and refresh the shared_objects size columns on object update, or recalculate the key totals on read rather than relying on the maintain-totals trigger alone.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

@pcfreak30
pcfreak30 merged commit 2d74458 into master Aug 31, 2026
7 of 12 checks passed
@pcfreak30
pcfreak30 deleted the sync/upstream branch August 31, 2026 08:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants