diff --git a/pkg/roles/mocks/Roles.go b/pkg/roles/mocks/Roles.go
new file mode 100644
index 0000000000000000000000000000000000000000..d7e61236e36d2a5f544950f0634c5204d4a30a3e
--- /dev/null
+++ b/pkg/roles/mocks/Roles.go
@@ -0,0 +1,112 @@
+// Code generated by mockery v2.7.4. DO NOT EDIT.
+
+package mocks
+
+import (
+	"context"
+
+	roles "git.perx.ru/perxis/perxis-go/pkg/roles"
+	"github.com/stretchr/testify/mock"
+)
+
+// Roles is an autogenerated mock type for the Roles type
+type Roles struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: ctx, role
+func (_m *Roles) Create(ctx context.Context, role *roles.Role) (*roles.Role, error) {
+	ret := _m.Called(ctx, role)
+
+	var r0 *roles.Role
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.Role) *roles.Role); ok {
+		r0 = rf(ctx, role)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*roles.Role)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *roles.Role) error); ok {
+		r1 = rf(ctx, role)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: ctx, spaceId, roleId
+func (_m *Roles) Delete(ctx context.Context, spaceId string, roleId string) error {
+	ret := _m.Called(ctx, spaceId, roleId)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
+		r0 = rf(ctx, spaceId, roleId)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// Get provides a mock function with given fields: ctx, spaceId, roleId
+func (_m *Roles) Get(ctx context.Context, spaceId string, roleId string) (*roles.Role, error) {
+	ret := _m.Called(ctx, spaceId, roleId)
+
+	var r0 *roles.Role
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) *roles.Role); ok {
+		r0 = rf(ctx, spaceId, roleId)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*roles.Role)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
+		r1 = rf(ctx, spaceId, roleId)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: ctx, spaceId
+func (_m *Roles) List(ctx context.Context, spaceId string) ([]*roles.Role, error) {
+	ret := _m.Called(ctx, spaceId)
+
+	var r0 []*roles.Role
+	if rf, ok := ret.Get(0).(func(context.Context, string) []*roles.Role); ok {
+		r0 = rf(ctx, spaceId)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*roles.Role)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
+		r1 = rf(ctx, spaceId)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: ctx, role
+func (_m *Roles) Update(ctx context.Context, role *roles.Role) error {
+	ret := _m.Called(ctx, role)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.Role) error); ok {
+		r0 = rf(ctx, role)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
diff --git a/pkg/roles/role.go b/pkg/roles/role.go
new file mode 100644
index 0000000000000000000000000000000000000000..4c284ee5d45026aedfc284ecc16e146e78b56bb2
--- /dev/null
+++ b/pkg/roles/role.go
@@ -0,0 +1,65 @@
+package roles
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/pkg/data"
+	"git.perx.ru/perxis/perxis-go/pkg/environments"
+	"git.perx.ru/perxis/perxis-go/pkg/permission"
+)
+
+const (
+	AnonymousRole  = "anonymous"
+	AuthorizedRole = "authorized"
+	ViewRole       = "view"
+)
+
+type Role struct {
+	// Внутренний идентификатор роли
+	ID string `json:"id" bson:"_id"`
+
+	// Идентификатор пространства
+	SpaceID string `json:"spaceId" bson:"-"`
+
+	// Описание роли, назначение
+	Description string `json:"description" bson:"description"`
+
+	// Список доступных окружений (ID или Alias)
+	Environments []string `json:"environments" bson:"environments"`
+
+	// Список правил доступа к коллекциям
+	Rules permission.Rules `json:"rules" bson:"rules"`
+
+	// Разрешить доступ API управления
+	AllowManagement bool `json:"allow_management" bson:"allow_management"`
+}
+
+func (r Role) CanAccessEnvironment(ctx context.Context, service environments.Environments, spaceID, envID string) bool {
+	if spaceID == "" || envID == "" {
+		return false
+	}
+
+	// Если явно не указаны доступные окружения - доступ по умолчанию к окружению master
+	if len(r.Environments) == 0 {
+		r.Environments = []string{environments.DefaultEnvironment}
+	}
+
+	if data.Contains(envID, r.Environments) {
+		return true
+	}
+
+	e, err := service.Get(ctx, spaceID, envID)
+	if err != nil || e == nil {
+		return false
+	}
+
+	aliases := append(e.Aliases, e.ID)
+
+	for _, ce := range r.Environments {
+		if data.Contains(ce, aliases) {
+			return true
+		}
+	}
+
+	return false
+}
diff --git a/pkg/roles/service.go b/pkg/roles/service.go
new file mode 100644
index 0000000000000000000000000000000000000000..b003008b10c7c0a0de6549c60262741fdf441f65
--- /dev/null
+++ b/pkg/roles/service.go
@@ -0,0 +1,16 @@
+package roles
+
+import (
+	"context"
+)
+
+// @microgen grpc
+// @protobuf git.perx.ru/perxis/perxis-go/proto/roles
+// @grpc-addr content.roles.Roles
+type Roles interface {
+	Create(ctx context.Context, role *Role) (created *Role, err error)
+	Get(ctx context.Context, spaceId, roleId string) (role *Role, err error)
+	List(ctx context.Context, spaceId string) (roles []*Role, err error)
+	Update(ctx context.Context, role *Role) (err error)
+	Delete(ctx context.Context, spaceId, roleId string) (err error)
+}
diff --git a/pkg/roles/transport/client.microgen.go b/pkg/roles/transport/client.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..71b1de9e2b1b746962effe61dc63c37cf7977e69
--- /dev/null
+++ b/pkg/roles/transport/client.microgen.go
@@ -0,0 +1,78 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import (
+	"context"
+	"errors"
+
+	roles "git.perx.ru/perxis/perxis-go/pkg/roles"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+func (set EndpointsSet) Create(arg0 context.Context, arg1 *roles.Role) (res0 *roles.Role, res1 error) {
+	request := CreateRequest{Role: arg1}
+	response, res1 := set.CreateEndpoint(arg0, &request)
+	if res1 != nil {
+		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res1 = errors.New(e.Message())
+		}
+		return
+	}
+	return response.(*CreateResponse).Created, res1
+}
+
+func (set EndpointsSet) Get(arg0 context.Context, arg1 string, arg2 string) (res0 *roles.Role, res1 error) {
+	request := GetRequest{
+		RoleId:  arg2,
+		SpaceId: arg1,
+	}
+	response, res1 := set.GetEndpoint(arg0, &request)
+	if res1 != nil {
+		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res1 = errors.New(e.Message())
+		}
+		return
+	}
+	return response.(*GetResponse).Role, res1
+}
+
+func (set EndpointsSet) List(arg0 context.Context, arg1 string) (res0 []*roles.Role, res1 error) {
+	request := ListRequest{SpaceId: arg1}
+	response, res1 := set.ListEndpoint(arg0, &request)
+	if res1 != nil {
+		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res1 = errors.New(e.Message())
+		}
+		return
+	}
+	return response.(*ListResponse).Roles, res1
+}
+
+func (set EndpointsSet) Update(arg0 context.Context, arg1 *roles.Role) (res0 error) {
+	request := UpdateRequest{Role: arg1}
+	_, res0 = set.UpdateEndpoint(arg0, &request)
+	if res0 != nil {
+		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res0 = errors.New(e.Message())
+		}
+		return
+	}
+	return res0
+}
+
+func (set EndpointsSet) Delete(arg0 context.Context, arg1 string, arg2 string) (res0 error) {
+	request := DeleteRequest{
+		RoleId:  arg2,
+		SpaceId: arg1,
+	}
+	_, res0 = set.DeleteEndpoint(arg0, &request)
+	if res0 != nil {
+		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res0 = errors.New(e.Message())
+		}
+		return
+	}
+	return res0
+}
diff --git a/pkg/roles/transport/endpoints.microgen.go b/pkg/roles/transport/endpoints.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..06fe337c3c48656eff8074237e5714c29eac8bb2
--- /dev/null
+++ b/pkg/roles/transport/endpoints.microgen.go
@@ -0,0 +1,14 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import endpoint "github.com/go-kit/kit/endpoint"
+
+// EndpointsSet implements Roles API and used for transport purposes.
+type EndpointsSet struct {
+	CreateEndpoint endpoint.Endpoint
+	GetEndpoint    endpoint.Endpoint
+	ListEndpoint   endpoint.Endpoint
+	UpdateEndpoint endpoint.Endpoint
+	DeleteEndpoint endpoint.Endpoint
+}
diff --git a/pkg/roles/transport/exchanges.microgen.go b/pkg/roles/transport/exchanges.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..bb7d8e5010877efb628ce34b4f89ae66857476eb
--- /dev/null
+++ b/pkg/roles/transport/exchanges.microgen.go
@@ -0,0 +1,42 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import roles "git.perx.ru/perxis/perxis-go/pkg/roles"
+
+type (
+	CreateRequest struct {
+		Role *roles.Role `json:"role"`
+	}
+	CreateResponse struct {
+		Created *roles.Role `json:"created"`
+	}
+
+	GetRequest struct {
+		SpaceId string `json:"space_id"`
+		RoleId  string `json:"role_id"`
+	}
+	GetResponse struct {
+		Role *roles.Role `json:"role"`
+	}
+
+	ListRequest struct {
+		SpaceId string `json:"space_id"`
+	}
+	ListResponse struct {
+		Roles []*roles.Role `json:"roles"`
+	}
+
+	UpdateRequest struct {
+		Role *roles.Role `json:"role"`
+	}
+	// Formal exchange type, please do not delete.
+	UpdateResponse struct{}
+
+	DeleteRequest struct {
+		SpaceId string `json:"space_id"`
+		RoleId  string `json:"role_id"`
+	}
+	// Formal exchange type, please do not delete.
+	DeleteResponse struct{}
+)
diff --git a/pkg/roles/transport/grpc/client.microgen.go b/pkg/roles/transport/grpc/client.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..747b1a8cada8b5e189e5e4c7994c79cb36df8b02
--- /dev/null
+++ b/pkg/roles/transport/grpc/client.microgen.go
@@ -0,0 +1,54 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/roles/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/roles"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	empty "github.com/golang/protobuf/ptypes/empty"
+	grpc "google.golang.org/grpc"
+)
+
+func NewGRPCClient(conn *grpc.ClientConn, addr string, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	if addr == "" {
+		addr = "content.roles.Roles"
+	}
+	return transport.EndpointsSet{
+		CreateEndpoint: grpckit.NewClient(
+			conn, addr, "Create",
+			_Encode_Create_Request,
+			_Decode_Create_Response,
+			pb.CreateResponse{},
+			opts...,
+		).Endpoint(),
+		DeleteEndpoint: grpckit.NewClient(
+			conn, addr, "Delete",
+			_Encode_Delete_Request,
+			_Decode_Delete_Response,
+			empty.Empty{},
+			opts...,
+		).Endpoint(),
+		GetEndpoint: grpckit.NewClient(
+			conn, addr, "Get",
+			_Encode_Get_Request,
+			_Decode_Get_Response,
+			pb.GetResponse{},
+			opts...,
+		).Endpoint(),
+		ListEndpoint: grpckit.NewClient(
+			conn, addr, "List",
+			_Encode_List_Request,
+			_Decode_List_Response,
+			pb.ListResponse{},
+			opts...,
+		).Endpoint(),
+		UpdateEndpoint: grpckit.NewClient(
+			conn, addr, "Update",
+			_Encode_Update_Request,
+			_Decode_Update_Response,
+			empty.Empty{},
+			opts...,
+		).Endpoint(),
+	}
+}
diff --git a/pkg/roles/transport/grpc/protobuf_endpoint_converters.microgen.go b/pkg/roles/transport/grpc/protobuf_endpoint_converters.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..aa66bf57105f290aae04a763c5a8e931a410fc53
--- /dev/null
+++ b/pkg/roles/transport/grpc/protobuf_endpoint_converters.microgen.go
@@ -0,0 +1,209 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+// Please, do not change functions names!
+package transportgrpc
+
+import (
+	"context"
+	"errors"
+
+	transport "git.perx.ru/perxis/perxis-go/pkg/roles/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/roles"
+	empty "github.com/golang/protobuf/ptypes/empty"
+)
+
+func _Encode_Create_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil CreateRequest")
+	}
+	req := request.(*transport.CreateRequest)
+	reqRole, err := PtrRoleToProto(req.Role)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.CreateRequest{Role: reqRole}, nil
+}
+
+func _Encode_Get_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil GetRequest")
+	}
+	req := request.(*transport.GetRequest)
+	return &pb.GetRequest{
+		RoleId:  req.RoleId,
+		SpaceId: req.SpaceId,
+	}, nil
+}
+
+func _Encode_List_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil ListRequest")
+	}
+	req := request.(*transport.ListRequest)
+	return &pb.ListRequest{SpaceId: req.SpaceId}, nil
+}
+
+func _Encode_Update_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil UpdateRequest")
+	}
+	req := request.(*transport.UpdateRequest)
+	reqRole, err := PtrRoleToProto(req.Role)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.UpdateRequest{Role: reqRole}, nil
+}
+
+func _Encode_Delete_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil DeleteRequest")
+	}
+	req := request.(*transport.DeleteRequest)
+	return &pb.DeleteRequest{
+		RoleId:  req.RoleId,
+		SpaceId: req.SpaceId,
+	}, nil
+}
+
+func _Encode_Get_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil GetResponse")
+	}
+	resp := response.(*transport.GetResponse)
+	respRole, err := PtrRoleToProto(resp.Role)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.GetResponse{Role: respRole}, nil
+}
+
+func _Encode_List_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil ListResponse")
+	}
+	resp := response.(*transport.ListResponse)
+	respRoles, err := ListPtrRoleToProto(resp.Roles)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.ListResponse{Roles: respRoles}, nil
+}
+
+func _Encode_Update_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Encode_Delete_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Decode_Create_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil CreateRequest")
+	}
+	req := request.(*pb.CreateRequest)
+	reqRole, err := ProtoToPtrRole(req.Role)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.CreateRequest{Role: reqRole}, nil
+}
+
+func _Decode_Get_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil GetRequest")
+	}
+	req := request.(*pb.GetRequest)
+	return &transport.GetRequest{
+		RoleId:  string(req.RoleId),
+		SpaceId: string(req.SpaceId),
+	}, nil
+}
+
+func _Decode_List_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil ListRequest")
+	}
+	req := request.(*pb.ListRequest)
+	return &transport.ListRequest{SpaceId: string(req.SpaceId)}, nil
+}
+
+func _Decode_Update_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil UpdateRequest")
+	}
+	req := request.(*pb.UpdateRequest)
+	reqRole, err := ProtoToPtrRole(req.Role)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.UpdateRequest{Role: reqRole}, nil
+}
+
+func _Decode_Delete_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil DeleteRequest")
+	}
+	req := request.(*pb.DeleteRequest)
+	return &transport.DeleteRequest{
+		RoleId:  string(req.RoleId),
+		SpaceId: string(req.SpaceId),
+	}, nil
+}
+
+func _Decode_Get_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil GetResponse")
+	}
+	resp := response.(*pb.GetResponse)
+	respRole, err := ProtoToPtrRole(resp.Role)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.GetResponse{Role: respRole}, nil
+}
+
+func _Decode_List_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil ListResponse")
+	}
+	resp := response.(*pb.ListResponse)
+	respRoles, err := ProtoToListPtrRole(resp.Roles)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.ListResponse{Roles: respRoles}, nil
+}
+
+func _Decode_Update_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Decode_Delete_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Encode_Create_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil CreateResponse")
+	}
+	resp := response.(*transport.CreateResponse)
+	respCreated, err := PtrRoleToProto(resp.Created)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.CreateResponse{Created: respCreated}, nil
+}
+
+func _Decode_Create_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil CreateResponse")
+	}
+	resp := response.(*pb.CreateResponse)
+	respCreated, err := ProtoToPtrRole(resp.Created)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.CreateResponse{Created: respCreated}, nil
+}
diff --git a/pkg/roles/transport/grpc/protobuf_type_converters.microgen.go b/pkg/roles/transport/grpc/protobuf_type_converters.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..10f207e5ebf3e07804009bf0bbb62401e0bdbf9a
--- /dev/null
+++ b/pkg/roles/transport/grpc/protobuf_type_converters.microgen.go
@@ -0,0 +1,110 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+// It is better for you if you do not change functions names!
+// This file will never be overwritten.
+package transportgrpc
+
+import (
+	permission "git.perx.ru/perxis/perxis-go/pkg/permission"
+	service "git.perx.ru/perxis/perxis-go/pkg/roles"
+	commonpb "git.perx.ru/perxis/perxis-go/proto/common"
+	pb "git.perx.ru/perxis/perxis-go/proto/roles"
+)
+
+func PtrRoleToProto(role *service.Role) (*pb.Role, error) {
+	if role == nil {
+		return nil, nil
+	}
+	rules := make([]*commonpb.Rule, 0, len(role.Rules))
+	for _, r := range role.Rules {
+		pr, _ := PtrPermissionRuleToProto(r)
+		rules = append(rules, pr)
+	}
+	return &pb.Role{
+		Id:              role.ID,
+		SpaceId:         role.SpaceID,
+		Description:     role.Description,
+		Rules:           rules,
+		Environments:    role.Environments,
+		AllowManagement: role.AllowManagement,
+	}, nil
+}
+
+func ProtoToPtrRole(protoRole *pb.Role) (*service.Role, error) {
+	if protoRole == nil {
+		return nil, nil
+	}
+
+	rules := make([]*permission.Rule, 0, len(protoRole.Rules))
+	for _, pr := range protoRole.Rules {
+		r, _ := ProtoToPtrPermissionRule(pr)
+		rules = append(rules, r)
+	}
+
+	return &service.Role{
+		ID:              protoRole.Id,
+		SpaceID:         protoRole.SpaceId,
+		Description:     protoRole.Description,
+		Rules:           rules,
+		Environments:    protoRole.Environments,
+		AllowManagement: protoRole.AllowManagement,
+	}, nil
+}
+
+func ListPtrRoleToProto(roles []*service.Role) ([]*pb.Role, error) {
+	protoRoles := make([]*pb.Role, 0, len(roles))
+	for _, r := range roles {
+		protoRole, _ := PtrRoleToProto(r)
+		protoRoles = append(protoRoles, protoRole)
+	}
+	return protoRoles, nil
+}
+
+func ProtoToListPtrRole(protoRoles []*pb.Role) ([]*service.Role, error) {
+	roles := make([]*service.Role, 0, len(protoRoles))
+	for _, r := range protoRoles {
+		role, _ := ProtoToPtrRole(r)
+		roles = append(roles, role)
+	}
+	return roles, nil
+}
+
+func PtrPermissionRuleToProto(rule *permission.Rule) (*commonpb.Rule, error) {
+	if rule == nil {
+		return nil, nil
+	}
+	actions := make([]commonpb.Action, 0, len(rule.Actions))
+	for _, a := range rule.Actions {
+		actions = append(actions, commonpb.Action(a))
+	}
+	return &commonpb.Rule{
+		CollectionId:    rule.CollectionID,
+		Actions:         actions,
+		Access:          commonpb.Access(rule.Access),
+		HiddenFields:    rule.HiddenFields,
+		ReadonlyFields:  rule.ReadonlyFields,
+		WriteonlyFields: rule.WriteonlyFields,
+		ReadFilter:      rule.ReadFilter,
+		WriteFilter:     rule.WriteFilter,
+	}, nil
+}
+
+func ProtoToPtrPermissionRule(protoRule *commonpb.Rule) (*permission.Rule, error) {
+	if protoRule == nil {
+		return nil, nil
+	}
+	actions := make([]permission.Action, 0, len(protoRule.Actions))
+	for _, a := range protoRule.Actions {
+		actions = append(actions, permission.Action(a))
+	}
+	return &permission.Rule{
+		CollectionID:    protoRule.CollectionId,
+		Actions:         actions,
+		Access:          permission.Access(protoRule.Access),
+		HiddenFields:    protoRule.HiddenFields,
+		ReadonlyFields:  protoRule.ReadonlyFields,
+		WriteonlyFields: protoRule.WriteonlyFields,
+		ReadFilter:      protoRule.ReadFilter,
+		WriteFilter:     protoRule.WriteFilter,
+	}, nil
+}
diff --git a/pkg/roles/transport/grpc/server.microgen.go b/pkg/roles/transport/grpc/server.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..dc012c75f1ff3a6965d9152f204c7d7f6b61d285
--- /dev/null
+++ b/pkg/roles/transport/grpc/server.microgen.go
@@ -0,0 +1,97 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+// DO NOT EDIT.
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/roles/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/roles"
+	grpc "github.com/go-kit/kit/transport/grpc"
+	empty "github.com/golang/protobuf/ptypes/empty"
+	context "golang.org/x/net/context"
+)
+
+type rolesServer struct {
+	create grpc.Handler
+	get    grpc.Handler
+	list   grpc.Handler
+	update grpc.Handler
+	delete grpc.Handler
+
+	pb.UnimplementedRolesServer
+}
+
+func NewGRPCServer(endpoints *transport.EndpointsSet, opts ...grpc.ServerOption) pb.RolesServer {
+	return &rolesServer{
+		create: grpc.NewServer(
+			endpoints.CreateEndpoint,
+			_Decode_Create_Request,
+			_Encode_Create_Response,
+			opts...,
+		),
+		delete: grpc.NewServer(
+			endpoints.DeleteEndpoint,
+			_Decode_Delete_Request,
+			_Encode_Delete_Response,
+			opts...,
+		),
+		get: grpc.NewServer(
+			endpoints.GetEndpoint,
+			_Decode_Get_Request,
+			_Encode_Get_Response,
+			opts...,
+		),
+		list: grpc.NewServer(
+			endpoints.ListEndpoint,
+			_Decode_List_Request,
+			_Encode_List_Response,
+			opts...,
+		),
+		update: grpc.NewServer(
+			endpoints.UpdateEndpoint,
+			_Decode_Update_Request,
+			_Encode_Update_Response,
+			opts...,
+		),
+	}
+}
+
+func (S *rolesServer) Create(ctx context.Context, req *pb.CreateRequest) (*pb.CreateResponse, error) {
+	_, resp, err := S.create.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*pb.CreateResponse), nil
+}
+
+func (S *rolesServer) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) {
+	_, resp, err := S.get.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*pb.GetResponse), nil
+}
+
+func (S *rolesServer) List(ctx context.Context, req *pb.ListRequest) (*pb.ListResponse, error) {
+	_, resp, err := S.list.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*pb.ListResponse), nil
+}
+
+func (S *rolesServer) Update(ctx context.Context, req *pb.UpdateRequest) (*empty.Empty, error) {
+	_, resp, err := S.update.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*empty.Empty), nil
+}
+
+func (S *rolesServer) Delete(ctx context.Context, req *pb.DeleteRequest) (*empty.Empty, error) {
+	_, resp, err := S.delete.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*empty.Empty), nil
+}
diff --git a/pkg/roles/transport/server.microgen.go b/pkg/roles/transport/server.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..119aae999a13bab884d4f98d09346b1ef2d13071
--- /dev/null
+++ b/pkg/roles/transport/server.microgen.go
@@ -0,0 +1,60 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import (
+	"context"
+
+	roles "git.perx.ru/perxis/perxis-go/pkg/roles"
+	endpoint "github.com/go-kit/kit/endpoint"
+)
+
+func Endpoints(svc roles.Roles) EndpointsSet {
+	return EndpointsSet{
+		CreateEndpoint: CreateEndpoint(svc),
+		DeleteEndpoint: DeleteEndpoint(svc),
+		GetEndpoint:    GetEndpoint(svc),
+		ListEndpoint:   ListEndpoint(svc),
+		UpdateEndpoint: UpdateEndpoint(svc),
+	}
+}
+
+func CreateEndpoint(svc roles.Roles) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*CreateRequest)
+		res0, res1 := svc.Create(arg0, req.Role)
+		return &CreateResponse{Created: res0}, res1
+	}
+}
+
+func GetEndpoint(svc roles.Roles) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*GetRequest)
+		res0, res1 := svc.Get(arg0, req.SpaceId, req.RoleId)
+		return &GetResponse{Role: res0}, res1
+	}
+}
+
+func ListEndpoint(svc roles.Roles) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*ListRequest)
+		res0, res1 := svc.List(arg0, req.SpaceId)
+		return &ListResponse{Roles: res0}, res1
+	}
+}
+
+func UpdateEndpoint(svc roles.Roles) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*UpdateRequest)
+		res0 := svc.Update(arg0, req.Role)
+		return &UpdateResponse{}, res0
+	}
+}
+
+func DeleteEndpoint(svc roles.Roles) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*DeleteRequest)
+		res0 := svc.Delete(arg0, req.SpaceId, req.RoleId)
+		return &DeleteResponse{}, res0
+	}
+}