~bigbes/sr-ht-dolt

ref: bd2d8a3680e62716440ea0165d2271d79f9d526e sr-ht-dolt/mcpsrv/ports.go -rw-r--r-- 6.3 KiB
bd2d8a36 — Eugene Blikh doltsrht: serve /query and the api-meta.json beside it 3 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package mcpsrv

import (
	"context"

	"sourcecraft.dev/bigbes/sr-ht-dolt/browse"
	"sourcecraft.dev/bigbes/sr-ht-dolt/core"
)

// The seams this package calls, declared consumer-side — the house style
// web/deps.go sets — so that the tools can be driven by an in-process MCP client
// over fakes, with no Postgres and no store on disk (docs/DESIGN.mcp.md §12).
//
// They name exactly what the tools call, and what is *absent* from them is the
// design of docs/DESIGN.mcp.md §2 made structural rather than remembered:
//
//   - No StoreManager. Nothing on this surface creates, initialises, evicts or
//     deletes an on-disk store, so no handler here can. A write tool added later
//     would have to widen this file first, which is a diff a reviewer sees.
//   - No ACL mutation, no repository create/update/delete. Repos below is the
//     read half of web's RepoStore and nothing more: a resolution, two listings
//     and the caller's effective grant.
//   - No key management. Dolt keys are a credential plane, and a surface reached
//     with one credential has no business enumerating another.
//   - No UserResolver. Resolving a username to a mirrored account is a write to
//     the local user table on first sight; this surface reads.
//   - No SQL engine anywhere. BrowseSession is the bare-store reader of
//     browse/, which is the only reading of a hosted database that exists
//     without a working set (docs/DESIGN.mcp.md §2, browse/open.go).
//
// The nil contract is the one every seam here shares and is not restated per
// method: a nil error means a usable result, no method returns (nil, nil) except
// EffectiveAccess — whose (nil, nil) *is* the answer "this caller holds no ACL
// entry", exactly as db.Store.EffectiveAccess defines it.

// Repos is the metadata store as this surface reads it: the repository rows and
// the caller's ACL grant on one of them.
//
// It is spelled with db.Store's own signatures, so *db.Store satisfies it as
// written and the production wiring binds it through the same request-scoped
// adapter web uses. There is no compile-time assertion here because that adapter
// is web's and unexported; the wiring commit is where the two meet.
//
// Visibility is *not* applied by these methods and must not be assumed from
// them. GetRepoByOwnerAndName answers about any repository that exists, and the
// caller here reproduces the browse dance — EffectiveAccess, core.Allowed,
// core.NotFoundForPrivate — exactly as web/router.go's loadRepoForBrowse does
// (docs/DESIGN.mcp.md §4.3). The two listings are the exception and say so
// themselves.
type Repos interface {
	// GetRepoByOwnerAndName resolves a database by its owner's username (without
	// the "~") and name, or reports db.ErrNotFound. It applies no visibility rule
	// whatsoever: what the caller may see is decided afterwards, by core.Allowed
	// over the grant EffectiveAccess returns.
	GetRepoByOwnerAndName(ctx context.Context, ownerUsername, name string) (*core.Repo, error)

	// ListReposByOwner lists one owner's databases that viewer may *list*, which
	// is a narrower rule than "may read": PUBLIC to everyone including anonymity,
	// plus anything viewer owns or holds an ACL entry on. An UNLISTED database of
	// somebody else is therefore absent from the listing and still readable by
	// direct address, which is the rule the dashboard already implements.
	//
	// viewer is nil for an anonymous caller, and anonymous is a normal caller
	// here — it sees the PUBLIC ones.
	ListReposByOwner(ctx context.Context, ownerUsername string, viewer *core.Caller) ([]*core.Repo, error)

	// ListReposForDashboard lists every database userID owns or holds an ACL
	// entry on, whatever its visibility. It is the signed-in caller's own view of
	// the instance and takes no viewer argument because the user *is* the viewer.
	ListReposForDashboard(ctx context.Context, userID int) ([]*core.Repo, error)

	// ListReposForViewer lists every database viewer may be shown, across all
	// owners. It is ListReposByOwner's rule minus the owner filter, and it is the
	// only enumeration on this seam that can answer an instance-wide question —
	// which is what ready_work with no database named asks (docs/DESIGN.mcp.md
	// §9.2, docs/DESIGN.views.md ch. 4).
	//
	// Listing is not authorization: the caller here still asks core.Allowed for
	// OpBrowse per database, exactly as web/handlers_ready.go does, before a
	// single store is opened. viewer is nil for an anonymous caller, and
	// anonymous is a normal caller — it gets the PUBLIC set.
	ListReposForViewer(ctx context.Context, viewer *core.Caller) ([]*core.Repo, error)

	// EffectiveAccess resolves the caller's ACL grant on a repository, or
	// (nil, nil) when there is none. Feed the result to core.Allowed; a nil grant
	// is not a denial, it is a fall-through to visibility.
	EffectiveAccess(ctx context.Context, userID, repoID int) (*core.AccessMode, error)
}

// BrowseSession is the read-only view of one bare store: the method set
// web/deps.go declares plus TableHash, so a *browse.DB satisfies both and the
// production adapter is the same one web uses.
//
// Everything here reads committed roots. There is no Write, no Commit and no
// working set to hold one — a bare NBS store has none (browse/open.go), which is
// what the whole pure-Go build stands on (docs/DESIGN.mcp.md §2).
type BrowseSession interface {
	Branches(ctx context.Context) ([]browse.Branch, error)
	Log(ctx context.Context, refStr, fromHash string, limit int) ([]browse.CommitInfo, string, error)
	Tables(ctx context.Context, refStr string) ([]browse.TableInfo, error)
	TableHash(ctx context.Context, refStr, table string) (string, bool, error)
	Rows(ctx context.Context, refStr, table string, offset, limit int) (*browse.RowPage, error)
	CommitSummary(ctx context.Context, hashStr string) (*browse.CommitDiff, error)
	Close() error
}

// BrowseOpener opens a session over the bare store at diskPath.
//
// Open is paired with Close by the handler that called it (defer sess.Close()),
// which is the browse discipline and not an ornament: a session is one fresh
// read of the on-disk manifest, so a handler sees commits the push writer landed
// since the last call and nothing is cached between calls (browse/open.go).
type BrowseOpener interface {
	Open(ctx context.Context, diskPath string) (BrowseSession, error)
}