local engine = require("assay.engine")
local function parse_args(argv)
local out = {}
local i = 1
while i <= #argv do
local a = argv[i]
if a == "--email" then out.email = argv[i + 1]; i = i + 2
elseif a == "--password" then out.password = argv[i + 1]; i = i + 2
elseif a == "--workflow-namespace" then out.ns = argv[i + 1]; i = i + 2
elseif a == "--engine-url" then out.engine_url = argv[i + 1]; i = i + 2
elseif a == "--admin-key" then out.admin_key = argv[i + 1]; i = i + 2
else error("init.lua: unknown arg " .. tostring(a)) end
end
if not out.email or not out.password then
error("init.lua: --email and --password are required")
end
return out
end
local args = parse_args(arg or {})
local engine_url = args.engine_url or env.get("ASSAY_ENGINE_URL")
local admin_key = args.admin_key or env.get("ASSAY_ADMIN_KEY")
if not engine_url or engine_url == "" then
error("init.lua: ASSAY_ENGINE_URL or --engine-url is required")
end
if not admin_key or admin_key == "" then
error("init.lua: ASSAY_ADMIN_KEY or --admin-key is required (break-glass)")
end
local e = engine.connect({ engine_url = engine_url, api_key = admin_key })
print("seeding zanzibar namespaces...")
e.auth.zanzibar:define_namespace({
name = "engine",
definitions = {
admin = { name = "admin", kind = { kind = "direct", value = {{ object_type = "user" }} } },
viewer = { name = "viewer", kind = { kind = "direct", value = {{ object_type = "user" }} } },
},
})
print(" engine: admin, viewer")
e.auth.zanzibar:define_namespace({
name = "auth",
definitions = {
admin = { name = "admin", kind = { kind = "direct", value = {{ object_type = "user" }} } },
},
})
print(" auth: admin")
e.auth.zanzibar:define_namespace({
name = "workflow",
definitions = {
admin = { name = "admin", kind = { kind = "direct", value = {{ object_type = "user" }} } },
user = { name = "user", kind = { kind = "direct", value = {{ object_type = "user" }} } },
viewer = { name = "viewer", kind = { kind = "direct", value = {{ object_type = "user" }} } },
access = {
name = "access",
kind = {
kind = "permission",
value = {
op = "union",
left = { op = "direct", relation = "admin" },
right = { op = "direct", relation = "user" },
},
},
},
},
})
print(" workflow: admin, user, viewer + access permission")
print("creating admin user " .. args.email .. "...")
local existing = nil
do
local list = e.auth.users:list({ search = args.email, limit = 50 })
if list and list.items then
for _, u in ipairs(list.items) do
if u.email == args.email then existing = u; break end
end
end
end
local user_id
if existing then
print(" user already exists (" .. existing.id .. "); resetting password")
e.auth.users:reset_password(existing.id, args.password)
user_id = existing.id
else
local user = e.auth.users:create({
email = args.email,
email_verified = true,
password = args.password,
})
user_id = user.id
print(" created " .. user_id)
end
print("granting operator tuples...")
local ns_workflow = args.ns or "main"
local tuples = {
{ object_type = "engine", object_id = "core", relation = "admin",
subject_type = "user", subject_id = user_id },
{ object_type = "auth", object_id = "system", relation = "admin",
subject_type = "user", subject_id = user_id },
{ object_type = "workflow", object_id = ns_workflow, relation = "admin",
subject_type = "user", subject_id = user_id },
}
for _, t in ipairs(tuples) do
e.auth.zanzibar:write(t)
print(" " .. t.object_type .. ":" .. t.object_id .. "#" .. t.relation
.. " @ user:" .. user_id)
end
print("")
print("✓ engine ready.")
print(" log in via POST /api/v1/engine/auth/login")
print(" { \"email\": \"" .. args.email .. "\", \"password\": \"...\" }")
print(" then remove auth.admin_api_keys from engine.toml.")