Skip to content

feat(vm): bind net-sftp — require "net/sftp" (Net::SFTP over injected SSH channel) - #203

Closed
tannevaled wants to merge 1 commit into
mainfrom
feat/bind-net-sftp
Closed

tannevaled wants to merge 1 commit into
mainfrom
feat/bind-net-sftp

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Summary

Binds the pure-Go SFTP wire codec go-ruby-net-sftp/net-sftp into rbgo as require "net/sftp", so Net::SFTP works with no .rb file.

SFTP runs over an SSH channel. The library owns only the deterministic packet codec (framing, the version-aware ATTRS struct, request-id correlation, version negotiation); the encrypted SSH transport is the host's job — the channel seam. rbgo has no go-ruby-ssh yet, so Net::SFTP.start / Session.new take an injected channel object responding to #write(bytes) / #read(n) (a duck-typed SSH channel, e.g. a StringIO), mirroring how Redis.new takes an injected IO connection. The binding writes FXP_INIT, negotiates the version from FXP_VERSION, then for each operation writes the request frame and reads the id-correlated response.

Ruby surface

  • Session: protocol_version/version, open!/close!/read!/write!, stat!/lstat!/fstat!/setstat!/fsetstat!, opendir!/readdir!, remove!/mkdir!/rmdir!/realpath!, rename!/readlink!/symlink!/link!/block!/unblock!, upload!/download!, dir, file.
  • Operations::Dir #foreach/#entries; Operations::FileFactory #open/#directory?; Operations::File #read/#write/#pos/#close.
  • Value objects: Protocol::V01::Attributes (size/uid/gid/owner/group/permissions/atime/mtime/type + directory?/file?/symlink?) and Name (name/filename/longname/attributes + predicates), with the v4+ longname synthesised on demand.
  • Errors: Net::SFTP::Exception < StandardError and Net::SFTP::StatusException carrying code/description/text/message; Net::SFTP::Constants (StatusCodes/PacketTypes).

Wiring

providedFeatures["net/sftp"], vm.registerNetSFTP() after registerNetHTTP (nests under the existing Net module), and classOf cases for the six wrapper types.

Verification

  • go build ./... and 6-arch build (amd64/arm64/riscv64/loong64/ppc64le/s390x) — no FAIL.
  • 100% coverage of the new files (net_sftp.go, net_sftp_bind.go) via a deterministic in-process channel whose reply frames are built with the same wire codec — no SSH, network, or goroutine.
  • Full internal/vm suite green; gofmt -l clean; go vet clean; -race clean.

🤖 Generated with Claude Code

… SSH channel)

Bind the pure-Go SFTP wire codec github.com/go-ruby-net-sftp/net-sftp into rbgo
as require "net/sftp", exposing the Ruby Net::SFTP surface.

SFTP runs over an SSH channel. The library owns only the deterministic packet
codec (framing, the version-aware ATTRS struct, request-id correlation, version
negotiation); the encrypted SSH transport is the host's job — the "channel seam".
rbgo has no go-ruby-ssh yet, so Net::SFTP.start / Session.new take an injected
channel object responding to #write(bytes) / #read(n) (a duck-typed SSH channel,
e.g. a StringIO), mirroring how Redis.new takes an injected IO connection.

The binding drives the library's request builders and response parsers over that
channel: it writes FXP_INIT, negotiates the version from FXP_VERSION, then for
each operation writes the request frame and reads the id-correlated response.

Surface:
- Session: protocol_version/version, open!/close!/read!/write!, stat!/lstat!/
  fstat!/setstat!/fsetstat!, opendir!/readdir!, remove!/mkdir!/rmdir!/realpath!,
  rename!/readlink!/symlink!/link!/block!/unblock!, upload!/download!, dir, file.
- Operations::Dir#foreach/#entries; Operations::FileFactory#open/#directory?;
  Operations::File#read/#write/#pos/#close.
- Value objects: Protocol::V01::Attributes (size/uid/gid/owner/group/permissions/
  atime/mtime/type + directory?/file?/symlink?) and Name (name/filename/longname/
  attributes + predicates), with v4+ longname synthesised on demand.
- Net::SFTP::Exception < StandardError and Net::SFTP::StatusException carrying
  code/description/text/message; Net::SFTP::Constants StatusCodes/PacketTypes.

Wired: providedFeatures["net/sftp"], vm.registerNetSFTP() after registerNetHTTP
(nests under the existing Net module), and classOf cases for the six wrapper
types. 100% coverage of the new files via a deterministic in-process channel
(Chan) whose reply frames are built with the same wire codec — no SSH, network,
or goroutine. gofmt/vet clean, -race clean, green on all six 64-bit arches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled

Copy link
Copy Markdown
Contributor Author

Superseded by the net-wave consolidation PR (single merge onto main; shared builtins/require/object_model). Work preserved verbatim; branch kept.

@tannevaled tannevaled closed this Jul 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant