agentv1

package
v0.22.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 18, 2026 License: BSD-3-Clause Imports: 10 Imported by: 0

Documentation

Index

Constants

View Source
const (
	AgentControlPlane_RegisterAgent_FullMethodName = "/weft.agent.v1.AgentControlPlane/RegisterAgent"
	AgentControlPlane_Heartbeat_FullMethodName     = "/weft.agent.v1.AgentControlPlane/Heartbeat"
	AgentControlPlane_AttachDrivers_FullMethodName = "/weft.agent.v1.AgentControlPlane/AttachDrivers"
)

Variables

View Source
var AgentControlPlane_ServiceDesc = grpc.ServiceDesc{
	ServiceName: "weft.agent.v1.AgentControlPlane",
	HandlerType: (*AgentControlPlaneServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "RegisterAgent",
			Handler:    _AgentControlPlane_RegisterAgent_Handler,
		},
		{
			MethodName: "Heartbeat",
			Handler:    _AgentControlPlane_Heartbeat_Handler,
		},
	},
	Streams: []grpc.StreamDesc{
		{
			StreamName:    "AttachDrivers",
			Handler:       _AgentControlPlane_AttachDrivers_Handler,
			ServerStreams: true,
			ClientStreams: true,
		},
	},
	Metadata: "agent.proto",
}

AgentControlPlane_ServiceDesc is the grpc.ServiceDesc for AgentControlPlane service. It's only intended for direct use with grpc.RegisterService, and not to be introspected or modified (even as a copy)

View Source
var File_agent_proto protoreflect.FileDescriptor

Functions

func RegisterAgentControlPlaneServer

func RegisterAgentControlPlaneServer(s grpc.ServiceRegistrar, srv AgentControlPlaneServer)

Types

type AgentControlPlaneClient

type AgentControlPlaneClient interface {
	// RegisterAgent + Heartbeat are the lifecycle calls. Unary
	// RPCs because they're rare + need explicit success/failure.
	RegisterAgent(ctx context.Context, in *RegisterAgentRequest, opts ...grpc.CallOption) (*RegisterAgentResponse, error)
	Heartbeat(ctx context.Context, in *HeartbeatRequest, opts ...grpc.CallOption) (*HeartbeatResponse, error)
	// AttachDrivers is the long-lived bidi stream the CP uses to
	// dispatch driver calls. Agent → first frame is the init
	// message ; after that, the stream is fully bidi.
	AttachDrivers(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[AttachDriversFrame, AttachDriversFrame], error)
}

AgentControlPlaneClient is the client API for AgentControlPlane service.

For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.

type AgentControlPlaneServer

type AgentControlPlaneServer interface {
	// RegisterAgent + Heartbeat are the lifecycle calls. Unary
	// RPCs because they're rare + need explicit success/failure.
	RegisterAgent(context.Context, *RegisterAgentRequest) (*RegisterAgentResponse, error)
	Heartbeat(context.Context, *HeartbeatRequest) (*HeartbeatResponse, error)
	// AttachDrivers is the long-lived bidi stream the CP uses to
	// dispatch driver calls. Agent → first frame is the init
	// message ; after that, the stream is fully bidi.
	AttachDrivers(grpc.BidiStreamingServer[AttachDriversFrame, AttachDriversFrame]) error
	// contains filtered or unexported methods
}

AgentControlPlaneServer is the server API for AgentControlPlane service. All implementations must embed UnimplementedAgentControlPlaneServer for forward compatibility.

type AgentControlPlane_AttachDriversClient

type AgentControlPlane_AttachDriversClient = grpc.BidiStreamingClient[AttachDriversFrame, AttachDriversFrame]

This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.

type AgentControlPlane_AttachDriversServer

type AgentControlPlane_AttachDriversServer = grpc.BidiStreamingServer[AttachDriversFrame, AttachDriversFrame]

This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.

type AgentDisconnect

type AgentDisconnect struct {
	Reason string `protobuf:"bytes,1,opt,name=reason,proto3" json:"reason,omitempty"`
	// contains filtered or unexported fields
}

func (*AgentDisconnect) Descriptor deprecated

func (*AgentDisconnect) Descriptor() ([]byte, []int)

Deprecated: Use AgentDisconnect.ProtoReflect.Descriptor instead.

func (*AgentDisconnect) GetReason

func (x *AgentDisconnect) GetReason() string

func (*AgentDisconnect) ProtoMessage

func (*AgentDisconnect) ProtoMessage()

func (*AgentDisconnect) ProtoReflect

func (x *AgentDisconnect) ProtoReflect() protoreflect.Message

func (*AgentDisconnect) Reset

func (x *AgentDisconnect) Reset()

func (*AgentDisconnect) String

func (x *AgentDisconnect) String() string

type AttachDriversFrame

type AttachDriversFrame struct {

	// Types that are valid to be assigned to Body:
	//
	//	*AttachDriversFrame_Init
	//	*AttachDriversFrame_Dispatch
	//	*AttachDriversFrame_Result
	//	*AttachDriversFrame_Disconnect
	Body isAttachDriversFrame_Body `protobuf_oneof:"body"`
	// contains filtered or unexported fields
}

AttachDriversFrame is the multiplexed envelope. The first frame on the stream is always init ; after that, dispatch + result alternate.

func (*AttachDriversFrame) Descriptor deprecated

func (*AttachDriversFrame) Descriptor() ([]byte, []int)

Deprecated: Use AttachDriversFrame.ProtoReflect.Descriptor instead.

func (*AttachDriversFrame) GetBody

func (x *AttachDriversFrame) GetBody() isAttachDriversFrame_Body

func (*AttachDriversFrame) GetDisconnect

func (x *AttachDriversFrame) GetDisconnect() *AgentDisconnect

func (*AttachDriversFrame) GetDispatch

func (x *AttachDriversFrame) GetDispatch() *DriverDispatchCall

func (*AttachDriversFrame) GetInit

func (x *AttachDriversFrame) GetInit() *AttachDriversInit

func (*AttachDriversFrame) GetResult

func (x *AttachDriversFrame) GetResult() *DriverDispatchResult

func (*AttachDriversFrame) ProtoMessage

func (*AttachDriversFrame) ProtoMessage()

func (*AttachDriversFrame) ProtoReflect

func (x *AttachDriversFrame) ProtoReflect() protoreflect.Message

func (*AttachDriversFrame) Reset

func (x *AttachDriversFrame) Reset()

func (*AttachDriversFrame) String

func (x *AttachDriversFrame) String() string

type AttachDriversFrame_Disconnect

type AttachDriversFrame_Disconnect struct {
	// disconnect frames close the stream cleanly. Either side
	// can send ; the other side acks then drops.
	Disconnect *AgentDisconnect `protobuf:"bytes,4,opt,name=disconnect,proto3,oneof"`
}

type AttachDriversFrame_Dispatch

type AttachDriversFrame_Dispatch struct {
	Dispatch *DriverDispatchCall `protobuf:"bytes,2,opt,name=dispatch,proto3,oneof"` // CP → agent
}

type AttachDriversFrame_Init

type AttachDriversFrame_Init struct {
	Init *AttachDriversInit `protobuf:"bytes,1,opt,name=init,proto3,oneof"`
}

type AttachDriversFrame_Result

type AttachDriversFrame_Result struct {
	Result *DriverDispatchResult `protobuf:"bytes,3,opt,name=result,proto3,oneof"` // agent → CP
}

type AttachDriversInit

type AttachDriversInit struct {
	HostUuid string `protobuf:"bytes,1,opt,name=host_uuid,json=hostUuid,proto3" json:"host_uuid,omitempty"`
	// Capability advertisement: which driver kinds this agent
	// can serve calls for. Control plane uses this to populate
	// the dispatch table's per-host driver tuple.
	DriverKinds []string `protobuf:"bytes,2,rep,name=driver_kinds,json=driverKinds,proto3" json:"driver_kinds,omitempty"` // {"hypervisor", "network", "volume", "image"}
	// contains filtered or unexported fields
}

func (*AttachDriversInit) Descriptor deprecated

func (*AttachDriversInit) Descriptor() ([]byte, []int)

Deprecated: Use AttachDriversInit.ProtoReflect.Descriptor instead.

func (*AttachDriversInit) GetDriverKinds

func (x *AttachDriversInit) GetDriverKinds() []string

func (*AttachDriversInit) GetHostUuid

func (x *AttachDriversInit) GetHostUuid() string

func (*AttachDriversInit) ProtoMessage

func (*AttachDriversInit) ProtoMessage()

func (*AttachDriversInit) ProtoReflect

func (x *AttachDriversInit) ProtoReflect() protoreflect.Message

func (*AttachDriversInit) Reset

func (x *AttachDriversInit) Reset()

func (*AttachDriversInit) String

func (x *AttachDriversInit) String() string

type DriverDispatchCall

type DriverDispatchCall struct {

	// call_id is a per-stream monotonic counter the agent echoes
	// back in DriverDispatchResult. Lets the CP correlate
	// concurrent calls on the same stream.
	CallId     uint64 `protobuf:"varint,1,opt,name=call_id,json=callId,proto3" json:"call_id,omitempty"`
	DriverKind string `protobuf:"bytes,2,opt,name=driver_kind,json=driverKind,proto3" json:"driver_kind,omitempty"` // "hypervisor" | "network" | "volume" | "image"
	MethodName string `protobuf:"bytes,3,opt,name=method_name,json=methodName,proto3" json:"method_name,omitempty"` // "CreateVM" | "AttachPort" | "EnsureVolume" | …
	Payload    []byte `protobuf:"bytes,4,opt,name=payload,proto3" json:"payload,omitempty"`                         // marshalled per-method request proto
	// contains filtered or unexported fields
}

DriverDispatchCall identifies which driver method on which driver kind the control plane wants the agent to invoke. The payload is opaque protobuf bytes — the agent decodes it against the per-method request type for the kind.

func (*DriverDispatchCall) Descriptor deprecated

func (*DriverDispatchCall) Descriptor() ([]byte, []int)

Deprecated: Use DriverDispatchCall.ProtoReflect.Descriptor instead.

func (*DriverDispatchCall) GetCallId

func (x *DriverDispatchCall) GetCallId() uint64

func (*DriverDispatchCall) GetDriverKind

func (x *DriverDispatchCall) GetDriverKind() string

func (*DriverDispatchCall) GetMethodName

func (x *DriverDispatchCall) GetMethodName() string

func (*DriverDispatchCall) GetPayload

func (x *DriverDispatchCall) GetPayload() []byte

func (*DriverDispatchCall) ProtoMessage

func (*DriverDispatchCall) ProtoMessage()

func (*DriverDispatchCall) ProtoReflect

func (x *DriverDispatchCall) ProtoReflect() protoreflect.Message

func (*DriverDispatchCall) Reset

func (x *DriverDispatchCall) Reset()

func (*DriverDispatchCall) String

func (x *DriverDispatchCall) String() string

type DriverDispatchResult

type DriverDispatchResult struct {
	CallId uint64 `protobuf:"varint,1,opt,name=call_id,json=callId,proto3" json:"call_id,omitempty"`
	// Exactly one of {payload, error_message} is set.
	Payload      []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` // marshalled per-method response proto
	ErrorMessage string `protobuf:"bytes,3,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"`
	// error_kind helps the caller distinguish driver-typed
	// errors from transport errors. Empty when no error.
	ErrorKind string `protobuf:"bytes,4,opt,name=error_kind,json=errorKind,proto3" json:"error_kind,omitempty"` // "" | "unsupported" | "not_applicable" | "not_found" | "internal"
	// contains filtered or unexported fields
}

func (*DriverDispatchResult) Descriptor deprecated

func (*DriverDispatchResult) Descriptor() ([]byte, []int)

Deprecated: Use DriverDispatchResult.ProtoReflect.Descriptor instead.

func (*DriverDispatchResult) GetCallId

func (x *DriverDispatchResult) GetCallId() uint64

func (*DriverDispatchResult) GetErrorKind

func (x *DriverDispatchResult) GetErrorKind() string

func (*DriverDispatchResult) GetErrorMessage

func (x *DriverDispatchResult) GetErrorMessage() string

func (*DriverDispatchResult) GetPayload

func (x *DriverDispatchResult) GetPayload() []byte

func (*DriverDispatchResult) ProtoMessage

func (*DriverDispatchResult) ProtoMessage()

func (*DriverDispatchResult) ProtoReflect

func (x *DriverDispatchResult) ProtoReflect() protoreflect.Message

func (*DriverDispatchResult) Reset

func (x *DriverDispatchResult) Reset()

func (*DriverDispatchResult) String

func (x *DriverDispatchResult) String() string

type HeartbeatRequest

type HeartbeatRequest struct {
	HostUuid string `protobuf:"bytes,1,opt,name=host_uuid,json=hostUuid,proto3" json:"host_uuid,omitempty"`
	// contains filtered or unexported fields
}

HeartbeatRequest is what the agent sends on its heartbeat timer. The control plane bumps last_seen and flips the host's state Down → Active if applicable.

func (*HeartbeatRequest) Descriptor deprecated

func (*HeartbeatRequest) Descriptor() ([]byte, []int)

Deprecated: Use HeartbeatRequest.ProtoReflect.Descriptor instead.

func (*HeartbeatRequest) GetHostUuid

func (x *HeartbeatRequest) GetHostUuid() string

func (*HeartbeatRequest) ProtoMessage

func (*HeartbeatRequest) ProtoMessage()

func (*HeartbeatRequest) ProtoReflect

func (x *HeartbeatRequest) ProtoReflect() protoreflect.Message

func (*HeartbeatRequest) Reset

func (x *HeartbeatRequest) Reset()

func (*HeartbeatRequest) String

func (x *HeartbeatRequest) String() string

type HeartbeatResponse

type HeartbeatResponse struct {
	// contains filtered or unexported fields
}

func (*HeartbeatResponse) Descriptor deprecated

func (*HeartbeatResponse) Descriptor() ([]byte, []int)

Deprecated: Use HeartbeatResponse.ProtoReflect.Descriptor instead.

func (*HeartbeatResponse) ProtoMessage

func (*HeartbeatResponse) ProtoMessage()

func (*HeartbeatResponse) ProtoReflect

func (x *HeartbeatResponse) ProtoReflect() protoreflect.Message

func (*HeartbeatResponse) Reset

func (x *HeartbeatResponse) Reset()

func (*HeartbeatResponse) String

func (x *HeartbeatResponse) String() string

type HostRegistration

type HostRegistration struct {

	// UUID is the agent's persisted host-uuid. Empty means
	// "mint a new one" ; set means "I am this host, refresh my
	// entry idempotently".
	Uuid           string            `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"`
	Hostname       string            `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"`
	Az             string            `protobuf:"bytes,3,opt,name=az,proto3" json:"az,omitempty"`
	Rack           string            `protobuf:"bytes,4,opt,name=rack,proto3" json:"rack,omitempty"`
	Endpoint       string            `protobuf:"bytes,5,opt,name=endpoint,proto3" json:"endpoint,omitempty"` // host:port the control plane can dial back if needed
	Hypervisor     string            `protobuf:"bytes,6,opt,name=hypervisor,proto3" json:"hypervisor,omitempty"`
	Architecture   string            `protobuf:"bytes,7,opt,name=architecture,proto3" json:"architecture,omitempty"`
	NetworkTypes   []string          `protobuf:"bytes,8,rep,name=network_types,json=networkTypes,proto3" json:"network_types,omitempty"`
	VolumeBackends []string          `protobuf:"bytes,9,rep,name=volume_backends,json=volumeBackends,proto3" json:"volume_backends,omitempty"`
	Properties     map[string]string `` /* 148-byte string literal not displayed */
	// agent_version is the weft binary's compile-time build version
	// (e.g. "v0.4.55"), stamped via -X main.version at link time.
	// Empty for unstamped dev builds. The control plane stores this
	// on the host registry so weft-tui / webui can show per-host
	// agent versions.
	AgentVersion string `protobuf:"bytes,11,opt,name=agent_version,json=agentVersion,proto3" json:"agent_version,omitempty"`
	// driver_versions maps each loaded driver's kind ("vz" / "qemu"
	// / "block" / …) to its compile-time build version, harvested
	// by the agent at startup from each plugin's HostInfo() RPC.
	// Empty for legacy / unstamped builds. Persisted on the host
	// registry alongside the agent_version.
	DriverVersions map[string]string `` /* 178-byte string literal not displayed */
	// OS / kernel / hardware facts collected at register time.
	// See weft.proto HostInfo for field semantics ; the agent
	// populates these via /etc/os-release + /proc/sys/kernel/osrelease
	// + /sys/class/net + statfs(/proc/mounts). Linux-only ; other
	// platforms ship zero values.
	OsId              string                         `protobuf:"bytes,13,opt,name=os_id,json=osId,proto3" json:"os_id,omitempty"`
	OsVersion         string                         `protobuf:"bytes,14,opt,name=os_version,json=osVersion,proto3" json:"os_version,omitempty"`
	OsPretty          string                         `protobuf:"bytes,15,opt,name=os_pretty,json=osPretty,proto3" json:"os_pretty,omitempty"`
	KernelVersion     string                         `protobuf:"bytes,16,opt,name=kernel_version,json=kernelVersion,proto3" json:"kernel_version,omitempty"`
	NetworkInterfaces []*weft_proto.NetworkInterface `protobuf:"bytes,17,rep,name=network_interfaces,json=networkInterfaces,proto3" json:"network_interfaces,omitempty"`
	StorageMounts     []*weft_proto.StorageMount     `protobuf:"bytes,18,rep,name=storage_mounts,json=storageMounts,proto3" json:"storage_mounts,omitempty"`
	CpuCount          int32                          `protobuf:"varint,19,opt,name=cpu_count,json=cpuCount,proto3" json:"cpu_count,omitempty"`
	MemoryMib         int64                          `protobuf:"varint,20,opt,name=memory_mib,json=memoryMib,proto3" json:"memory_mib,omitempty"`
	Gpus              []*weft_proto.GPU              `protobuf:"bytes,21,rep,name=gpus,proto3" json:"gpus,omitempty"`
	// contains filtered or unexported fields
}

HostRegistration is the agent's identity statement: who it is, what host it runs on, what capabilities the host has. Mirrors weft/agent.HostRegistration.

func (*HostRegistration) Descriptor deprecated

func (*HostRegistration) Descriptor() ([]byte, []int)

Deprecated: Use HostRegistration.ProtoReflect.Descriptor instead.

func (*HostRegistration) GetAgentVersion added in v0.18.0

func (x *HostRegistration) GetAgentVersion() string

func (*HostRegistration) GetArchitecture

func (x *HostRegistration) GetArchitecture() string

func (*HostRegistration) GetAz

func (x *HostRegistration) GetAz() string

func (*HostRegistration) GetCpuCount added in v0.20.0

func (x *HostRegistration) GetCpuCount() int32

func (*HostRegistration) GetDriverVersions added in v0.18.0

func (x *HostRegistration) GetDriverVersions() map[string]string

func (*HostRegistration) GetEndpoint

func (x *HostRegistration) GetEndpoint() string

func (*HostRegistration) GetGpus added in v0.20.0

func (x *HostRegistration) GetGpus() []*weft_proto.GPU

func (*HostRegistration) GetHostname

func (x *HostRegistration) GetHostname() string

func (*HostRegistration) GetHypervisor

func (x *HostRegistration) GetHypervisor() string

func (*HostRegistration) GetKernelVersion added in v0.19.0

func (x *HostRegistration) GetKernelVersion() string

func (*HostRegistration) GetMemoryMib added in v0.20.0

func (x *HostRegistration) GetMemoryMib() int64

func (*HostRegistration) GetNetworkInterfaces added in v0.19.0

func (x *HostRegistration) GetNetworkInterfaces() []*weft_proto.NetworkInterface

func (*HostRegistration) GetNetworkTypes

func (x *HostRegistration) GetNetworkTypes() []string

func (*HostRegistration) GetOsId added in v0.19.0

func (x *HostRegistration) GetOsId() string

func (*HostRegistration) GetOsPretty added in v0.19.0

func (x *HostRegistration) GetOsPretty() string

func (*HostRegistration) GetOsVersion added in v0.19.0

func (x *HostRegistration) GetOsVersion() string

func (*HostRegistration) GetProperties

func (x *HostRegistration) GetProperties() map[string]string

func (*HostRegistration) GetRack

func (x *HostRegistration) GetRack() string

func (*HostRegistration) GetStorageMounts added in v0.19.0

func (x *HostRegistration) GetStorageMounts() []*weft_proto.StorageMount

func (*HostRegistration) GetUuid

func (x *HostRegistration) GetUuid() string

func (*HostRegistration) GetVolumeBackends

func (x *HostRegistration) GetVolumeBackends() []string

func (*HostRegistration) ProtoMessage

func (*HostRegistration) ProtoMessage()

func (*HostRegistration) ProtoReflect

func (x *HostRegistration) ProtoReflect() protoreflect.Message

func (*HostRegistration) Reset

func (x *HostRegistration) Reset()

func (*HostRegistration) String

func (x *HostRegistration) String() string

type RegisterAgentRequest

type RegisterAgentRequest struct {
	Registration *HostRegistration `protobuf:"bytes,1,opt,name=registration,proto3" json:"registration,omitempty"`
	// contains filtered or unexported fields
}

func (*RegisterAgentRequest) Descriptor deprecated

func (*RegisterAgentRequest) Descriptor() ([]byte, []int)

Deprecated: Use RegisterAgentRequest.ProtoReflect.Descriptor instead.

func (*RegisterAgentRequest) GetRegistration

func (x *RegisterAgentRequest) GetRegistration() *HostRegistration

func (*RegisterAgentRequest) ProtoMessage

func (*RegisterAgentRequest) ProtoMessage()

func (*RegisterAgentRequest) ProtoReflect

func (x *RegisterAgentRequest) ProtoReflect() protoreflect.Message

func (*RegisterAgentRequest) Reset

func (x *RegisterAgentRequest) Reset()

func (*RegisterAgentRequest) String

func (x *RegisterAgentRequest) String() string

type RegisterAgentResponse

type RegisterAgentResponse struct {

	// assigned_uuid is the UUID the control plane registered the
	// host under. Matches request.registration.uuid in the
	// idempotent re-register case ; differs only when the request
	// came in with an empty uuid (the CP minted a fresh one).
	AssignedUuid string `protobuf:"bytes,1,opt,name=assigned_uuid,json=assignedUuid,proto3" json:"assigned_uuid,omitempty"`
	// contains filtered or unexported fields
}

func (*RegisterAgentResponse) Descriptor deprecated

func (*RegisterAgentResponse) Descriptor() ([]byte, []int)

Deprecated: Use RegisterAgentResponse.ProtoReflect.Descriptor instead.

func (*RegisterAgentResponse) GetAssignedUuid

func (x *RegisterAgentResponse) GetAssignedUuid() string

func (*RegisterAgentResponse) ProtoMessage

func (*RegisterAgentResponse) ProtoMessage()

func (*RegisterAgentResponse) ProtoReflect

func (x *RegisterAgentResponse) ProtoReflect() protoreflect.Message

func (*RegisterAgentResponse) Reset

func (x *RegisterAgentResponse) Reset()

func (*RegisterAgentResponse) String

func (x *RegisterAgentResponse) String() string

type UnimplementedAgentControlPlaneServer

type UnimplementedAgentControlPlaneServer struct{}

UnimplementedAgentControlPlaneServer must be embedded to have forward compatible implementations.

NOTE: this should be embedded by value instead of pointer to avoid a nil pointer dereference when methods are called.

func (UnimplementedAgentControlPlaneServer) AttachDrivers

func (UnimplementedAgentControlPlaneServer) Heartbeat

func (UnimplementedAgentControlPlaneServer) RegisterAgent

type UnsafeAgentControlPlaneServer

type UnsafeAgentControlPlaneServer interface {
	// contains filtered or unexported methods
}

UnsafeAgentControlPlaneServer may be embedded to opt out of forward compatibility for this service. Use of this interface is not recommended, as added methods to AgentControlPlaneServer will result in compilation errors.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL