Permissions
By default a command is owner-only. You opt into wider access deliberately.
Owner-only is the default
func (m *Weather) CommandMetas() map[string]goroku.CommandMeta {
return map[string]goroku.CommandMeta{
"weather": {OnlyOwner: true},
}
}OnlyOwner is checked on identity alone — it ignores sudo, temporary grants and group masks. Use it for anything that touches your account, files or shell.
Permission masks
For commands others may use, Goroku has a bitmask system. The owner assigns masks at runtime with .security; your module declares nothing unless you want a different default.
| Constant | Grants |
|---|---|
OWNER | The account owner |
SUDO | Users on the sudo list |
GROUP_OWNER | The group's creator |
GROUP_ADMIN | Any admin |
GROUP_ADMIN_BAN_USERS | Admins with ban rights |
GROUP_ADMIN_DEL_MSGS | Admins with delete rights |
GROUP_ADMIN_PIN_MSGS | Admins with pin rights |
GROUP_ADMIN_ADD_ADMINS | Admins who can promote |
GROUP_ADMIN_CHANGE_INFO | Admins who can edit info |
GROUP_ADMIN_INVITE | Admins who can invite |
GROUP_MEMBER | Any group member |
PM | Anyone in private chat |
EVERYONE | Anyone, anywhere |
They combine:
GROUP_ADMIN_BAN_USERS | GROUP_OWNEREVERYONE means everyone
EVERYONE lets any stranger in any chat run the command against your account. Reach for GROUP_MEMBER or a specific admin right first.
Checking inside a handler
func (m *Mod) DangerousCmd(msg *goroku.Message) error {
sm := m.Client.GetSecurityManager()
if sm == nil || !sm.IsOwner(msg.SenderID) {
return msg.Answer(m.T("owner_only", "🚫 <b>Owner only</b>"))
}
return m.doIt(msg)
}Prefer the declarative OnlyOwner — the dispatcher rejects before your code runs, so there is no path where you forget the check.
Writing a module others will install
Someone installing your module gives it their Telegram session and their server. Things worth doing:
- Default to the narrowest permission. Widen only where the feature needs it.
- Never log secrets. Mark config fields
Secret: true(Configuration). - Escape user input before putting it in HTML (Answering).
- Validate before acting. A user-supplied chat ID or path deserves a check.
- Do not shell out to user input. If you must run a command, pass arguments as a slice — never build a shell string.
// Fine: arguments are separate, no shell involved.
exec.CommandContext(ctx, "ffmpeg", "-i", path, out)
// Not fine: a filename with a quote becomes a shell injection.
exec.CommandContext(ctx, "sh", "-c", "ffmpeg -i "+path)Remote code
Modules loaded over the network are HTTPS-only, and private, loopback, link-local and CGNAT addresses are refused — the resolved IP is checked, not just the hostname, so DNS tricks do not help.
That protects the transport. It says nothing about whether the code is safe. A native module is arbitrary Go running in your process; read it before you install it.