extension

package
v1.2.3 Latest Latest
Warning

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

Go to latest
Published: Dec 2, 2024 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Index

Constants

View Source
const (
	EnvoyGatewayExtension_PostRouteModify_FullMethodName        = "/envoygateway.extension.EnvoyGatewayExtension/PostRouteModify"
	EnvoyGatewayExtension_PostVirtualHostModify_FullMethodName  = "/envoygateway.extension.EnvoyGatewayExtension/PostVirtualHostModify"
	EnvoyGatewayExtension_PostHTTPListenerModify_FullMethodName = "/envoygateway.extension.EnvoyGatewayExtension/PostHTTPListenerModify"
	EnvoyGatewayExtension_PostTranslateModify_FullMethodName    = "/envoygateway.extension.EnvoyGatewayExtension/PostTranslateModify"
)

Variables

View Source
var EnvoyGatewayExtension_ServiceDesc = grpc.ServiceDesc{
	ServiceName: "envoygateway.extension.EnvoyGatewayExtension",
	HandlerType: (*EnvoyGatewayExtensionServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "PostRouteModify",
			Handler:    _EnvoyGatewayExtension_PostRouteModify_Handler,
		},
		{
			MethodName: "PostVirtualHostModify",
			Handler:    _EnvoyGatewayExtension_PostVirtualHostModify_Handler,
		},
		{
			MethodName: "PostHTTPListenerModify",
			Handler:    _EnvoyGatewayExtension_PostHTTPListenerModify_Handler,
		},
		{
			MethodName: "PostTranslateModify",
			Handler:    _EnvoyGatewayExtension_PostTranslateModify_Handler,
		},
	},
	Streams:  []grpc.StreamDesc{},
	Metadata: "proto/extension/service.proto",
}

EnvoyGatewayExtension_ServiceDesc is the grpc.ServiceDesc for EnvoyGatewayExtension 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_proto_extension_context_proto protoreflect.FileDescriptor
View Source
var File_proto_extension_service_proto protoreflect.FileDescriptor

Functions

func RegisterEnvoyGatewayExtensionServer

func RegisterEnvoyGatewayExtensionServer(s grpc.ServiceRegistrar, srv EnvoyGatewayExtensionServer)

Types

type EnvoyGatewayExtensionClient

type EnvoyGatewayExtensionClient interface {
	// PostRouteModify provides a way for extensions to modify a route generated by Envoy Gateway before it is finalized.
	// Doing so allows extensions to configure/modify route fields configured by Envoy Gateway and also to configure the
	// Route's TypedPerFilterConfig which may be desirable to do things such as pass settings and information to
	// ext_authz filters.
	// PostRouteModify also passes a list of Unstructured data for the externalRefs owned by the extension on the HTTPRoute that
	// created this xDS route
	// PostRouteModify will only be executed if an extension is loaded and only on Routes which were generated from an HTTPRoute
	// that uses extension resources as externalRef filters.
	PostRouteModify(ctx context.Context, in *PostRouteModifyRequest, opts ...grpc.CallOption) (*PostRouteModifyResponse, error)
	// PostVirtualHostModify provides a way for extensions to modify a VirtualHost generated by Envoy Gateway before it is finalized.
	// An extension can also make use of this hook to generate and insert entirely new Routes not generated by Envoy Gateway.
	// PostVirtualHostModify is always executed when an extension is loaded. An extension may return nil to not make any changes
	// to it.
	PostVirtualHostModify(ctx context.Context, in *PostVirtualHostModifyRequest, opts ...grpc.CallOption) (*PostVirtualHostModifyResponse, error)
	// PostHTTPListenerModify allows an extension to make changes to a Listener generated by Envoy Gateway before it is finalized.
	// PostHTTPListenerModify is always executed when an extension is loaded. An extension may return nil
	// in order to not make any changes to it.
	PostHTTPListenerModify(ctx context.Context, in *PostHTTPListenerModifyRequest, opts ...grpc.CallOption) (*PostHTTPListenerModifyResponse, error)
	// PostTranslateModify allows an extension to modify the clusters and secrets in the xDS config.
	// This allows for inserting clusters that may change along with extension specific configuration to be dynamically created rather than
	// using custom bootstrap config which would be sufficient for clusters that are static and not prone to have their configurations changed.
	// An example of how this may be used is to inject a cluster that will be used by an ext_authz http filter created by the extension.
	// The list of clusters and secrets returned by the extension are used as the final list of all clusters and secrets
	// PostTranslateModify is always executed when an extension is loaded
	PostTranslateModify(ctx context.Context, in *PostTranslateModifyRequest, opts ...grpc.CallOption) (*PostTranslateModifyResponse, error)
}

EnvoyGatewayExtensionClient is the client API for EnvoyGatewayExtension 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 EnvoyGatewayExtensionServer

type EnvoyGatewayExtensionServer interface {
	// PostRouteModify provides a way for extensions to modify a route generated by Envoy Gateway before it is finalized.
	// Doing so allows extensions to configure/modify route fields configured by Envoy Gateway and also to configure the
	// Route's TypedPerFilterConfig which may be desirable to do things such as pass settings and information to
	// ext_authz filters.
	// PostRouteModify also passes a list of Unstructured data for the externalRefs owned by the extension on the HTTPRoute that
	// created this xDS route
	// PostRouteModify will only be executed if an extension is loaded and only on Routes which were generated from an HTTPRoute
	// that uses extension resources as externalRef filters.
	PostRouteModify(context.Context, *PostRouteModifyRequest) (*PostRouteModifyResponse, error)
	// PostVirtualHostModify provides a way for extensions to modify a VirtualHost generated by Envoy Gateway before it is finalized.
	// An extension can also make use of this hook to generate and insert entirely new Routes not generated by Envoy Gateway.
	// PostVirtualHostModify is always executed when an extension is loaded. An extension may return nil to not make any changes
	// to it.
	PostVirtualHostModify(context.Context, *PostVirtualHostModifyRequest) (*PostVirtualHostModifyResponse, error)
	// PostHTTPListenerModify allows an extension to make changes to a Listener generated by Envoy Gateway before it is finalized.
	// PostHTTPListenerModify is always executed when an extension is loaded. An extension may return nil
	// in order to not make any changes to it.
	PostHTTPListenerModify(context.Context, *PostHTTPListenerModifyRequest) (*PostHTTPListenerModifyResponse, error)
	// PostTranslateModify allows an extension to modify the clusters and secrets in the xDS config.
	// This allows for inserting clusters that may change along with extension specific configuration to be dynamically created rather than
	// using custom bootstrap config which would be sufficient for clusters that are static and not prone to have their configurations changed.
	// An example of how this may be used is to inject a cluster that will be used by an ext_authz http filter created by the extension.
	// The list of clusters and secrets returned by the extension are used as the final list of all clusters and secrets
	// PostTranslateModify is always executed when an extension is loaded
	PostTranslateModify(context.Context, *PostTranslateModifyRequest) (*PostTranslateModifyResponse, error)
	// contains filtered or unexported methods
}

EnvoyGatewayExtensionServer is the server API for EnvoyGatewayExtension service. All implementations must embed UnimplementedEnvoyGatewayExtensionServer for forward compatibility

type ExtensionResource

type ExtensionResource struct {
	UnstructuredBytes []byte `protobuf:"bytes,1,opt,name=unstructured_bytes,json=unstructuredBytes,proto3" json:"unstructured_bytes,omitempty"`
	// contains filtered or unexported fields
}

ExtensionResource stores the data for a K8s API object referenced in an HTTPRouteFilter extensionRef. It is constructed from an unstructured.Unstructured marshalled to JSON. An extension can marshal the bytes from this resource back into an unstructured.Unstructured and then perform type checking to obtain the resource.

func (*ExtensionResource) Descriptor deprecated

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

Deprecated: Use ExtensionResource.ProtoReflect.Descriptor instead.

func (*ExtensionResource) GetUnstructuredBytes

func (x *ExtensionResource) GetUnstructuredBytes() []byte

func (*ExtensionResource) ProtoMessage

func (*ExtensionResource) ProtoMessage()

func (*ExtensionResource) ProtoReflect

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

func (*ExtensionResource) Reset

func (x *ExtensionResource) Reset()

func (*ExtensionResource) String

func (x *ExtensionResource) String() string

type PostHTTPListenerExtensionContext

type PostHTTPListenerExtensionContext struct {

	// Resources introduced by the extension that were used as extension server
	// policies targeting the listener
	ExtensionResources []*ExtensionResource `protobuf:"bytes,1,rep,name=extension_resources,json=extensionResources,proto3" json:"extension_resources,omitempty"`
	// contains filtered or unexported fields
}

Empty for now but we can add fields to the context as use-cases are discovered without breaking any clients that use the API additional context information can be added to this message as more use-cases are discovered

func (*PostHTTPListenerExtensionContext) Descriptor deprecated

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

Deprecated: Use PostHTTPListenerExtensionContext.ProtoReflect.Descriptor instead.

func (*PostHTTPListenerExtensionContext) GetExtensionResources added in v1.1.0

func (x *PostHTTPListenerExtensionContext) GetExtensionResources() []*ExtensionResource

func (*PostHTTPListenerExtensionContext) ProtoMessage

func (*PostHTTPListenerExtensionContext) ProtoMessage()

func (*PostHTTPListenerExtensionContext) ProtoReflect

func (*PostHTTPListenerExtensionContext) Reset

func (*PostHTTPListenerExtensionContext) String

type PostHTTPListenerModifyRequest

type PostHTTPListenerModifyRequest struct {
	Listener            *v31.Listener                     `protobuf:"bytes,1,opt,name=listener,proto3" json:"listener,omitempty"`
	PostListenerContext *PostHTTPListenerExtensionContext `protobuf:"bytes,2,opt,name=post_listener_context,json=postListenerContext,proto3" json:"post_listener_context,omitempty"`
	// contains filtered or unexported fields
}

PostVirtualHostModifyRequest sends a Listener that was generated by Envoy Gateway along with context information to an extension so that the Listener can be modified

func (*PostHTTPListenerModifyRequest) Descriptor deprecated

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

Deprecated: Use PostHTTPListenerModifyRequest.ProtoReflect.Descriptor instead.

func (*PostHTTPListenerModifyRequest) GetListener

func (x *PostHTTPListenerModifyRequest) GetListener() *v31.Listener

func (*PostHTTPListenerModifyRequest) GetPostListenerContext

func (*PostHTTPListenerModifyRequest) ProtoMessage

func (*PostHTTPListenerModifyRequest) ProtoMessage()

func (*PostHTTPListenerModifyRequest) ProtoReflect

func (*PostHTTPListenerModifyRequest) Reset

func (x *PostHTTPListenerModifyRequest) Reset()

func (*PostHTTPListenerModifyRequest) String

type PostHTTPListenerModifyResponse

type PostHTTPListenerModifyResponse struct {
	Listener *v31.Listener `protobuf:"bytes,1,opt,name=listener,proto3" json:"listener,omitempty"`
	// contains filtered or unexported fields
}

PostHTTPListenerModifyResponse is the expected response from an extension and contains a modified version of the Listener that was sent If an extension returns a nil Listener then it will not be modified

func (*PostHTTPListenerModifyResponse) Descriptor deprecated

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

Deprecated: Use PostHTTPListenerModifyResponse.ProtoReflect.Descriptor instead.

func (*PostHTTPListenerModifyResponse) GetListener

func (x *PostHTTPListenerModifyResponse) GetListener() *v31.Listener

func (*PostHTTPListenerModifyResponse) ProtoMessage

func (*PostHTTPListenerModifyResponse) ProtoMessage()

func (*PostHTTPListenerModifyResponse) ProtoReflect

func (*PostHTTPListenerModifyResponse) Reset

func (x *PostHTTPListenerModifyResponse) Reset()

func (*PostHTTPListenerModifyResponse) String

type PostRouteExtensionContext

type PostRouteExtensionContext struct {

	// Resources introduced by the extension that were used as extensionRefs in an HTTPRoute/GRPCRoute
	ExtensionResources []*ExtensionResource `protobuf:"bytes,1,rep,name=extension_resources,json=extensionResources,proto3" json:"extension_resources,omitempty"`
	// hostnames are the fully qualified domain names attached to the HTTPRoute
	Hostnames []string `protobuf:"bytes,2,rep,name=hostnames,proto3" json:"hostnames,omitempty"`
	// contains filtered or unexported fields
}

RouteExtensionContext provides resources introduced by an extension and watched by Envoy Gateway additional context information can be added to this message as more use-cases are discovered

func (*PostRouteExtensionContext) Descriptor deprecated

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

Deprecated: Use PostRouteExtensionContext.ProtoReflect.Descriptor instead.

func (*PostRouteExtensionContext) GetExtensionResources

func (x *PostRouteExtensionContext) GetExtensionResources() []*ExtensionResource

func (*PostRouteExtensionContext) GetHostnames

func (x *PostRouteExtensionContext) GetHostnames() []string

func (*PostRouteExtensionContext) ProtoMessage

func (*PostRouteExtensionContext) ProtoMessage()

func (*PostRouteExtensionContext) ProtoReflect

func (*PostRouteExtensionContext) Reset

func (x *PostRouteExtensionContext) Reset()

func (*PostRouteExtensionContext) String

func (x *PostRouteExtensionContext) String() string

type PostRouteModifyRequest

type PostRouteModifyRequest struct {
	Route            *v3.Route                  `protobuf:"bytes,1,opt,name=route,proto3" json:"route,omitempty"`
	PostRouteContext *PostRouteExtensionContext `protobuf:"bytes,2,opt,name=post_route_context,json=postRouteContext,proto3" json:"post_route_context,omitempty"`
	// contains filtered or unexported fields
}

PostRouteModifyRequest sends a Route that was generated by Envoy Gateway along with context information to an extension so that the Route can be modified

func (*PostRouteModifyRequest) Descriptor deprecated

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

Deprecated: Use PostRouteModifyRequest.ProtoReflect.Descriptor instead.

func (*PostRouteModifyRequest) GetPostRouteContext

func (x *PostRouteModifyRequest) GetPostRouteContext() *PostRouteExtensionContext

func (*PostRouteModifyRequest) GetRoute

func (x *PostRouteModifyRequest) GetRoute() *v3.Route

func (*PostRouteModifyRequest) ProtoMessage

func (*PostRouteModifyRequest) ProtoMessage()

func (*PostRouteModifyRequest) ProtoReflect

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

func (*PostRouteModifyRequest) Reset

func (x *PostRouteModifyRequest) Reset()

func (*PostRouteModifyRequest) String

func (x *PostRouteModifyRequest) String() string

type PostRouteModifyResponse

type PostRouteModifyResponse struct {
	Route *v3.Route `protobuf:"bytes,1,opt,name=route,proto3" json:"route,omitempty"`
	// contains filtered or unexported fields
}

PostRouteModifyResponse is the expected response from an extension and contains a modified version of the Route that was sent If an extension returns a nil Route then it will not be modified

func (*PostRouteModifyResponse) Descriptor deprecated

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

Deprecated: Use PostRouteModifyResponse.ProtoReflect.Descriptor instead.

func (*PostRouteModifyResponse) GetRoute

func (x *PostRouteModifyResponse) GetRoute() *v3.Route

func (*PostRouteModifyResponse) ProtoMessage

func (*PostRouteModifyResponse) ProtoMessage()

func (*PostRouteModifyResponse) ProtoReflect

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

func (*PostRouteModifyResponse) Reset

func (x *PostRouteModifyResponse) Reset()

func (*PostRouteModifyResponse) String

func (x *PostRouteModifyResponse) String() string

type PostTranslateExtensionContext

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

Empty for now but we can add fields to the context as use-cases are discovered without breaking any clients that use the API additional context information can be added to this message as more use-cases are discovered

func (*PostTranslateExtensionContext) Descriptor deprecated

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

Deprecated: Use PostTranslateExtensionContext.ProtoReflect.Descriptor instead.

func (*PostTranslateExtensionContext) ProtoMessage

func (*PostTranslateExtensionContext) ProtoMessage()

func (*PostTranslateExtensionContext) ProtoReflect

func (*PostTranslateExtensionContext) Reset

func (x *PostTranslateExtensionContext) Reset()

func (*PostTranslateExtensionContext) String

type PostTranslateModifyRequest

type PostTranslateModifyRequest struct {
	PostTranslateContext *PostTranslateExtensionContext `protobuf:"bytes,1,opt,name=post_translate_context,json=postTranslateContext,proto3" json:"post_translate_context,omitempty"`
	Clusters             []*v32.Cluster                 `protobuf:"bytes,2,rep,name=clusters,proto3" json:"clusters,omitempty"`
	Secrets              []*v33.Secret                  `protobuf:"bytes,3,rep,name=secrets,proto3" json:"secrets,omitempty"`
	// contains filtered or unexported fields
}

PostTranslateModifyRequest currently sends only clusters and secrets to an extension. The extension is free to add/modify/remove the resources it received.

func (*PostTranslateModifyRequest) Descriptor deprecated

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

Deprecated: Use PostTranslateModifyRequest.ProtoReflect.Descriptor instead.

func (*PostTranslateModifyRequest) GetClusters

func (x *PostTranslateModifyRequest) GetClusters() []*v32.Cluster

func (*PostTranslateModifyRequest) GetPostTranslateContext

func (x *PostTranslateModifyRequest) GetPostTranslateContext() *PostTranslateExtensionContext

func (*PostTranslateModifyRequest) GetSecrets

func (x *PostTranslateModifyRequest) GetSecrets() []*v33.Secret

func (*PostTranslateModifyRequest) ProtoMessage

func (*PostTranslateModifyRequest) ProtoMessage()

func (*PostTranslateModifyRequest) ProtoReflect

func (*PostTranslateModifyRequest) Reset

func (x *PostTranslateModifyRequest) Reset()

func (*PostTranslateModifyRequest) String

func (x *PostTranslateModifyRequest) String() string

type PostTranslateModifyResponse

type PostTranslateModifyResponse struct {
	Clusters []*v32.Cluster `protobuf:"bytes,1,rep,name=clusters,proto3" json:"clusters,omitempty"`
	Secrets  []*v33.Secret  `protobuf:"bytes,2,rep,name=secrets,proto3" json:"secrets,omitempty"`
	// contains filtered or unexported fields
}

PostTranslateModifyResponse is the expected response from an extension and contains the full list of xDS clusters and secrets to be used for the xDS config.

func (*PostTranslateModifyResponse) Descriptor deprecated

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

Deprecated: Use PostTranslateModifyResponse.ProtoReflect.Descriptor instead.

func (*PostTranslateModifyResponse) GetClusters

func (x *PostTranslateModifyResponse) GetClusters() []*v32.Cluster

func (*PostTranslateModifyResponse) GetSecrets

func (x *PostTranslateModifyResponse) GetSecrets() []*v33.Secret

func (*PostTranslateModifyResponse) ProtoMessage

func (*PostTranslateModifyResponse) ProtoMessage()

func (*PostTranslateModifyResponse) ProtoReflect

func (*PostTranslateModifyResponse) Reset

func (x *PostTranslateModifyResponse) Reset()

func (*PostTranslateModifyResponse) String

func (x *PostTranslateModifyResponse) String() string

type PostVirtualHostExtensionContext

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

Empty for now but we can add fields to the context as use-cases are discovered without breaking any clients that use the API additional context information can be added to this message as more use-cases are discovered

func (*PostVirtualHostExtensionContext) Descriptor deprecated

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

Deprecated: Use PostVirtualHostExtensionContext.ProtoReflect.Descriptor instead.

func (*PostVirtualHostExtensionContext) ProtoMessage

func (*PostVirtualHostExtensionContext) ProtoMessage()

func (*PostVirtualHostExtensionContext) ProtoReflect

func (*PostVirtualHostExtensionContext) Reset

func (*PostVirtualHostExtensionContext) String

type PostVirtualHostModifyRequest

type PostVirtualHostModifyRequest struct {
	VirtualHost            *v3.VirtualHost                  `protobuf:"bytes,1,opt,name=virtual_host,json=virtualHost,proto3" json:"virtual_host,omitempty"`
	PostVirtualHostContext *PostVirtualHostExtensionContext `` /* 131-byte string literal not displayed */
	// contains filtered or unexported fields
}

PostVirtualHostModifyRequest sends a VirtualHost that was generated by Envoy Gateway along with context information to an extension so that the VirtualHost can be modified

func (*PostVirtualHostModifyRequest) Descriptor deprecated

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

Deprecated: Use PostVirtualHostModifyRequest.ProtoReflect.Descriptor instead.

func (*PostVirtualHostModifyRequest) GetPostVirtualHostContext

func (x *PostVirtualHostModifyRequest) GetPostVirtualHostContext() *PostVirtualHostExtensionContext

func (*PostVirtualHostModifyRequest) GetVirtualHost

func (x *PostVirtualHostModifyRequest) GetVirtualHost() *v3.VirtualHost

func (*PostVirtualHostModifyRequest) ProtoMessage

func (*PostVirtualHostModifyRequest) ProtoMessage()

func (*PostVirtualHostModifyRequest) ProtoReflect

func (*PostVirtualHostModifyRequest) Reset

func (x *PostVirtualHostModifyRequest) Reset()

func (*PostVirtualHostModifyRequest) String

type PostVirtualHostModifyResponse

type PostVirtualHostModifyResponse struct {
	VirtualHost *v3.VirtualHost `protobuf:"bytes,1,opt,name=virtual_host,json=virtualHost,proto3" json:"virtual_host,omitempty"`
	// contains filtered or unexported fields
}

PostVirtualHostModifyResponse is the expected response from an extension and contains a modified version of the VirtualHost that was sent If an extension returns a nil Virtual Host then it will not be modified

func (*PostVirtualHostModifyResponse) Descriptor deprecated

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

Deprecated: Use PostVirtualHostModifyResponse.ProtoReflect.Descriptor instead.

func (*PostVirtualHostModifyResponse) GetVirtualHost

func (x *PostVirtualHostModifyResponse) GetVirtualHost() *v3.VirtualHost

func (*PostVirtualHostModifyResponse) ProtoMessage

func (*PostVirtualHostModifyResponse) ProtoMessage()

func (*PostVirtualHostModifyResponse) ProtoReflect

func (*PostVirtualHostModifyResponse) Reset

func (x *PostVirtualHostModifyResponse) Reset()

func (*PostVirtualHostModifyResponse) String

type UnimplementedEnvoyGatewayExtensionServer

type UnimplementedEnvoyGatewayExtensionServer struct {
}

UnimplementedEnvoyGatewayExtensionServer must be embedded to have forward compatible implementations.

func (UnimplementedEnvoyGatewayExtensionServer) PostRouteModify

func (UnimplementedEnvoyGatewayExtensionServer) PostTranslateModify

func (UnimplementedEnvoyGatewayExtensionServer) PostVirtualHostModify

type UnsafeEnvoyGatewayExtensionServer

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

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

Jump to

Keyboard shortcuts

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