[{"data":1,"prerenderedAt":16243},["ShallowReactive",2],{"blog":3,"\u002Fblog":11},{"id":4,"title":5,"description":6,"extension":7,"meta":8,"stem":9,"__hash__":10},"blog\u002Fblog.yml","Blog","All of the posts are written for educational purposes only.","yml",{},"blog","FpxzUqQSogoB_EJycq1_f-L1F-K3F74GrKJdgHraqNU",[12,203,790,1003,4522,4871,5574,6299,7273,7442,13829,14113,14269,15047,15274,15593],{"id":13,"title":14,"author":15,"badge":20,"body":22,"date":191,"description":192,"draft":193,"extension":194,"image":195,"meta":197,"navigation":198,"path":199,"seo":200,"stem":201,"__hash__":202},"posts\u002Fblog\u002F2025\u002Fsubwatch.md","I built a thing",{"name":16,"to":17,"avatar":18},"Nir Galon","https:\u002F\u002Fx.com\u002Fnirgn975",{"src":19},"\u002Favatar.webp",{"label":21},"solopreneur",{"type":23,"value":24,"toc":187},"minimark",[25,29,32,35,38,48,51,54,112,127,134,137,140,147,150,169,172,179],[26,27,28],"p",{},"I was minding my own business and scrolling X (formally Twitter) when I encountered a post from a designer that put subscriptions logos on top of a calendar month. The UI\u002FUX was stunning! People were loving it and the post got a lot of likes, replies and basically a lot of engagement.",[26,30,31],{},"Right then and there I decided to do a thing - build it. I limit myself to 1 month to release an MVP and see if people are liking it and decided that the pricing model should be a “pay once own it for life”, because it’s kinda funny to pay subscription to monitor other subscriptions.",[26,33,34],{},"The technology I chose to do it is a Chrome Extension, because I can store your data locally and don’t need a server (helps me with the pricing model of paying once) and I can inject a button to your Gmail and get the subscriptions data from the receipt email (it's also good timing).",[26,36,37],{},"I was off to the races, and it’ll not be an understatement to say that I underestimated the amount of work I thought would be required. Nonetheless I finished the Extension and the website a few days before my deadline and I was thrilled!",[26,39,40],{},[41,42],"img",{"alt":43,"className":44,"src":47},"Calendar view",[45,46],"rounded-lg","mx-auto","\u002Fposts\u002F2025\u002Fsubwatch\u002Fdemo-1.jpg",[26,49,50],{},"This is my first product that I’m launching, charging for it, and actively searching for customers. I was a true indie hacker \u002F solopreneur now. But how do I get customers? How do I tell the world about it?",[26,52,53],{},"I got back to X and started asking people, joined indie hackers communities, started to follow some solopreneurs and read their posts, and then I realized that distribution is really hard, maybe even harder than building the product.",[26,55,56,57,64,65,64,70,64,75,64,80,64,85,64,90,64,95,64,100,105,106,111],{},"I started writing replies to posts about related topics and insert my extension if it’s appropriate. I listed my extension on ",[58,59,63],"a",{"href":60,"rel":61},"https:\u002F\u002Fwww.producthunt.com\u002Fproducts\u002Fsubwatch-3",[62],"nofollow","product hunt",", ",[58,66,69],{"href":67,"rel":68},"https:\u002F\u002Fbetalist.com\u002Fstartups\u002Fsubwatch",[62],"betalist",[58,71,74],{"href":72,"rel":73},"https:\u002F\u002Fwww.chromehunt.app\u002Fextensions\u002Fsubwatch",[62],"chromehunt",[58,76,79],{"href":77,"rel":78},"https:\u002F\u002Fmicrolaunch.net\u002Fp\u002Fsubwatch-fd4fd5",[62],"microlaunch",[58,81,84],{"href":82,"rel":83},"https:\u002F\u002Fwww.uneed.best",[62],"uneed",[58,86,89],{"href":87,"rel":88},"https:\u002F\u002Fdealhunt.cc",[62],"dealhunt",[58,91,94],{"href":92,"rel":93},"https:\u002F\u002Fpitchwall.co",[62],"pitchwall",[58,96,99],{"href":97,"rel":98},"https:\u002F\u002Fwww.tinylaun.ch",[62],"tinylaun.ch",[58,101,104],{"href":102,"rel":103},"https:\u002F\u002Fwww.launchingnext.com",[62],"launchingnext",", and ",[58,107,110],{"href":108,"rel":109},"https:\u002F\u002Fwww.saashub.com\u002Fsubwatch-co-alternatives",[62],"saashub"," And I got a few sales 🥳",[26,113,114,115,120,121,126],{},"I also added the website to other platforms, to get backlinks. Like ",[58,116,119],{"href":117,"rel":118},"https:\u002F\u002Fland-book.com\u002Fwebsites\u002F74241-subwatch-subwatch-website",[62],"land book"," and ",[58,122,125],{"href":123,"rel":124},"https:\u002F\u002Fdribbble.com\u002Fshots\u002F25671033-Subwatch-Turn-subscription-chaos-into-financial-harmony",[62],"dribbble",".",[26,128,129],{},[41,130],{"alt":131,"className":132,"src":133},"Insights view",[45,46],"\u002Fposts\u002F2025\u002Fsubwatch\u002Fdemo-2.jpg",[26,135,136],{},"The sales were the fuel to keep me going. Every email notification about a new sale was a celebration. I can’t describe the feeling of someone paying for my product. But there was one thing that bugged me: I was out of ideas about features and updates. Sure, I caught a few bugs and fixed them, but what’s next?",[26,138,139],{},"And then the ideas started to come to me, from paying users, in the form of feature requests. I was so happy I got to work immediately and hammered most of them in a week! Even updated the website to reflect some of the new features.",[26,141,142],{},[41,143],{"alt":144,"className":145,"src":146},"Alerts",[45,46],"\u002Fposts\u002F2025\u002Fsubwatch\u002Fdemo-3.jpg",[26,148,149],{},"Where am I going from here?",[151,152,153,157,160,163,166],"ul",{},[154,155,156],"li",{},"Keep finding free directories and launch my extension in them.",[154,158,159],{},"Working on the website SEO.",[154,161,162],{},"Find Subreddit and Facebook groups to post in (I’m mainly focused on X in my personal life so most of the traffic comes from there at the moment).",[154,164,165],{},"Will add a 7 days trial.",[154,167,168],{},"And I have one other big idea for the product that I’m considering implementing but not sure yet.",[26,170,171],{},"To all of my readers that want to support and buy it, here is a 10% discount code for the first 100 users: BLOG10",[26,173,174,175],{},"The link to the product: ",[58,176,177],{"href":177,"rel":178},"https:\u002F\u002Fsubwatch.co",[62],[26,180,181,182],{},"The link to the chrome webstore: ",[58,183,186],{"href":184,"rel":185},"https:\u002F\u002Fchromewebstore.google.com\u002Fdetail\u002Fsubwatch\u002Ffnbokglphlnngocknlhgbjcpgjjboeie",[62],"chrome webstore",{"title":188,"searchDepth":189,"depth":189,"links":190},"",2,[],"2025-02-21","Building and launching Subwatch, a pay-once Chrome extension for subscriptions.",false,"md",{"src":196},"\u002Fposts\u002F2025\u002Fsubwatch\u002Fcover.jpeg",{},true,"\u002Fblog\u002F2025\u002Fsubwatch",{"title":14,"description":192},"blog\u002F2025\u002Fsubwatch","KVnqGZb_A9N5HJAXhHiIS_62Gv52Tw_3SJ0eBG3ufTI",{"id":204,"title":205,"author":206,"badge":208,"body":210,"date":781,"description":782,"draft":193,"extension":194,"image":783,"meta":785,"navigation":198,"path":786,"seo":787,"stem":788,"__hash__":789},"posts\u002Fblog\u002F2024\u002Fsupabase-functions.md","Supabase functions",{"name":16,"to":17,"avatar":207},{"src":19},{"label":209},"development",{"type":23,"value":211,"toc":775},[212,226,235,238,241,436,457,462,468,521,526,559,571,578,582,599,678,693,719,723,743,764,768,771],[26,213,214,215,220,221,126],{},"Recently I started to work with ",[58,216,219],{"href":217,"rel":218},"https:\u002F\u002Fsupabase.com",[62],"Supabase",", which I must admit is a joy to use!, but I was stuck on some logic I wanted to do which is to create an organization and a user profile when a new user signs up. So naturally I started to read about supabase functions which are ",[58,222,225],{"href":223,"rel":224},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Ffunctions.html",[62],"PostgreSQL functions",[26,227,228,229,234],{},"It wasn't a very easy thing to do, since I hadn't used ",[58,230,233],{"href":231,"rel":232},"https:\u002F\u002Fwww.postgresql.org",[62],"PostgreSQL"," since version 9 and I didn't know about all the latest and greatest, but that didn't stop me from Google searching it, asking ChatGPT about it, watching some YouTube tutorials, and reading some blog posts on it - I couldn't find anything close to what I needed to achieve. So I put on some old school music and got to work - played with it for hours while reading the PostgreSQL documentation, and finally I succeeded!",[26,236,237],{},"As I said before, I couldn't find anyone that published something close to it, so I figured I have to.",[26,239,240],{},"The signup method looks like this:",[242,243,248],"pre",{"className":244,"code":245,"filename":246,"language":247,"meta":188,"style":188},"language-javascript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const { data, error } = await supabase.auth.signUp({\n  email: localState.email,\n  password: localState.password,\n  options: {\n    data: {\n      first_name: localState.firstName,\n      last_name: localState.lastName,\n      organization_name: localState.organizationName,\n    },\n  },\n});\n","AppRegister.vue","javascript",[249,250,251,304,324,341,352,362,379,396,413,419,425],"code",{"__ignoreMap":188},[252,253,256,260,264,268,271,274,277,280,284,287,289,292,294,298,301],"span",{"class":254,"line":255},"line",1,[252,257,259],{"class":258},"spNyl","const",[252,261,263],{"class":262},"sMK4o"," {",[252,265,267],{"class":266},"sTEyZ"," data",[252,269,270],{"class":262},",",[252,272,273],{"class":266}," error ",[252,275,276],{"class":262},"}",[252,278,279],{"class":262}," =",[252,281,283],{"class":282},"s7zQu"," await",[252,285,286],{"class":266}," supabase",[252,288,126],{"class":262},[252,290,291],{"class":266},"auth",[252,293,126],{"class":262},[252,295,297],{"class":296},"s2Zo4","signUp",[252,299,300],{"class":266},"(",[252,302,303],{"class":262},"{\n",[252,305,306,310,313,316,318,321],{"class":254,"line":189},[252,307,309],{"class":308},"swJcz","  email",[252,311,312],{"class":262},":",[252,314,315],{"class":266}," localState",[252,317,126],{"class":262},[252,319,320],{"class":266},"email",[252,322,323],{"class":262},",\n",[252,325,327,330,332,334,336,339],{"class":254,"line":326},3,[252,328,329],{"class":308},"  password",[252,331,312],{"class":262},[252,333,315],{"class":266},[252,335,126],{"class":262},[252,337,338],{"class":266},"password",[252,340,323],{"class":262},[252,342,344,347,349],{"class":254,"line":343},4,[252,345,346],{"class":308},"  options",[252,348,312],{"class":262},[252,350,351],{"class":262}," {\n",[252,353,355,358,360],{"class":254,"line":354},5,[252,356,357],{"class":308},"    data",[252,359,312],{"class":262},[252,361,351],{"class":262},[252,363,365,368,370,372,374,377],{"class":254,"line":364},6,[252,366,367],{"class":308},"      first_name",[252,369,312],{"class":262},[252,371,315],{"class":266},[252,373,126],{"class":262},[252,375,376],{"class":266},"firstName",[252,378,323],{"class":262},[252,380,382,385,387,389,391,394],{"class":254,"line":381},7,[252,383,384],{"class":308},"      last_name",[252,386,312],{"class":262},[252,388,315],{"class":266},[252,390,126],{"class":262},[252,392,393],{"class":266},"lastName",[252,395,323],{"class":262},[252,397,399,402,404,406,408,411],{"class":254,"line":398},8,[252,400,401],{"class":308},"      organization_name",[252,403,312],{"class":262},[252,405,315],{"class":266},[252,407,126],{"class":262},[252,409,410],{"class":266},"organizationName",[252,412,323],{"class":262},[252,414,416],{"class":254,"line":415},9,[252,417,418],{"class":262},"    },\n",[252,420,422],{"class":254,"line":421},10,[252,423,424],{"class":262},"  },\n",[252,426,428,430,433],{"class":254,"line":427},11,[252,429,276],{"class":262},[252,431,432],{"class":266},")",[252,434,435],{"class":262},";\n",[26,437,438,439,120,442,445,446,449,450,445,453,456],{},"As you can see, there is the email and password, which is passed to the signUp method, but the extra information is ",[249,440,441],{},"first_name",[249,443,444],{},"last_name"," which I wanted to put in the ",[249,447,448],{},"profiles"," table, and the ",[249,451,452],{},"organization_name",[249,454,455],{},"organizations"," table. And, of course, linked them all together with some old school foreign keys.",[458,459,461],"h2",{"id":460},"_1-tables","1. Tables",[26,463,464,465,467],{},"So, first let's create the ",[249,466,448],{}," table:",[242,469,474],{"className":470,"code":471,"filename":472,"language":473,"meta":188,"style":188},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","create table profiles (\n  id uuid references auth.users on delete cascade not null primary key,\n  created_at timestamp with time zone,\n  updated_at timestamp with time zone,\n  first_name text,\n  last_name text,\n  phone text,\n  organization_id uuid references public.organizations on delete cascade not null,\n);\n","createProfiles.sql","sql",[249,475,476,481,486,491,496,501,506,511,516],{"__ignoreMap":188},[252,477,478],{"class":254,"line":255},[252,479,480],{},"create table profiles (\n",[252,482,483],{"class":254,"line":189},[252,484,485],{},"  id uuid references auth.users on delete cascade not null primary key,\n",[252,487,488],{"class":254,"line":326},[252,489,490],{},"  created_at timestamp with time zone,\n",[252,492,493],{"class":254,"line":343},[252,494,495],{},"  updated_at timestamp with time zone,\n",[252,497,498],{"class":254,"line":354},[252,499,500],{},"  first_name text,\n",[252,502,503],{"class":254,"line":364},[252,504,505],{},"  last_name text,\n",[252,507,508],{"class":254,"line":381},[252,509,510],{},"  phone text,\n",[252,512,513],{"class":254,"line":398},[252,514,515],{},"  organization_id uuid references public.organizations on delete cascade not null,\n",[252,517,518],{"class":254,"line":415},[252,519,520],{},");\n",[26,522,523,524,467],{},"And now let's create the ",[249,525,455],{},[242,527,530],{"className":470,"code":528,"filename":529,"language":473,"meta":188,"style":188},"create table organizations (\n  id uuid not null primary key,\n  created_at timestamp with time zone,\n  updated_at timestamp with time zone,\n  name text,\n);\n","createOrganizations.sql",[249,531,532,537,542,546,550,555],{"__ignoreMap":188},[252,533,534],{"class":254,"line":255},[252,535,536],{},"create table organizations (\n",[252,538,539],{"class":254,"line":189},[252,540,541],{},"  id uuid not null primary key,\n",[252,543,544],{"class":254,"line":326},[252,545,490],{},[252,547,548],{"class":254,"line":343},[252,549,495],{},[252,551,552],{"class":254,"line":354},[252,553,554],{},"  name text,\n",[252,556,557],{"class":254,"line":364},[252,558,520],{},[26,560,561,562,564,565,567,568,570],{},"As you can see the ",[249,563,448],{}," table is the one linking between the user from ",[249,566,291],{}," table and the organization from ",[249,569,455],{}," table. And you can head over to the Database tab to see the schema in a graphical representation with Supabase Schema Visualizer:",[26,572,573],{},[41,574],{"alt":575,"className":576,"src":577},"Schema Visualizer",[45,46],"\u002Fposts\u002F2024\u002Fsupabase-functions\u002Fschema-visualizer.webp",[458,579,581],{"id":580},"_2-function","2. Function",[26,583,584,585,587,588,590,591,593,594,120,596,598],{},"Now it's time to create the function itself. The function should get the ",[249,586,452],{}," and create a new organization in the ",[249,589,455],{}," table. We should save the organization's newly created id to then use it when we create a new profile in the ",[249,592,448],{}," table, along with the ",[249,595,441],{},[249,597,444],{}," from the signUp extra options.",[242,600,603],{"className":470,"code":601,"filename":602,"language":473,"meta":188,"style":188},"create or replace function handle_new_org_and_profile() returns trigger as $$\ndeclare\n  org_id uuid;\nbegin\n  -- insert new organization\n  insert into public.organizations(name)\n  values (new.raw_user_meta_data->>'organization_name')\n  returning id into org_id;\n  --  insert new profile\n  insert into public.profiles (id, first_name, last_name, organization_id)\n  values (new.id, new.raw_user_meta_data->>'first_name', new.raw_user_meta_data->>'last_name', org_id);\n  return new;\nend;\n$$ language plpgsql security definer;\n","supabaseTrigger.sql",[249,604,605,610,615,620,625,630,635,640,645,650,655,660,666,672],{"__ignoreMap":188},[252,606,607],{"class":254,"line":255},[252,608,609],{},"create or replace function handle_new_org_and_profile() returns trigger as $$\n",[252,611,612],{"class":254,"line":189},[252,613,614],{},"declare\n",[252,616,617],{"class":254,"line":326},[252,618,619],{},"  org_id uuid;\n",[252,621,622],{"class":254,"line":343},[252,623,624],{},"begin\n",[252,626,627],{"class":254,"line":354},[252,628,629],{},"  -- insert new organization\n",[252,631,632],{"class":254,"line":364},[252,633,634],{},"  insert into public.organizations(name)\n",[252,636,637],{"class":254,"line":381},[252,638,639],{},"  values (new.raw_user_meta_data->>'organization_name')\n",[252,641,642],{"class":254,"line":398},[252,643,644],{},"  returning id into org_id;\n",[252,646,647],{"class":254,"line":415},[252,648,649],{},"  --  insert new profile\n",[252,651,652],{"class":254,"line":421},[252,653,654],{},"  insert into public.profiles (id, first_name, last_name, organization_id)\n",[252,656,657],{"class":254,"line":427},[252,658,659],{},"  values (new.id, new.raw_user_meta_data->>'first_name', new.raw_user_meta_data->>'last_name', org_id);\n",[252,661,663],{"class":254,"line":662},12,[252,664,665],{},"  return new;\n",[252,667,669],{"class":254,"line":668},13,[252,670,671],{},"end;\n",[252,673,675],{"class":254,"line":674},14,[252,676,677],{},"$$ language plpgsql security definer;\n",[26,679,680,681,684,685,688,689,692],{},"The object from the signUp call is hosted in this ",[249,682,683],{},"new"," variable, and the extra options are in the ",[249,686,687],{},"raw_user_meta_data"," field. The two separate insert statements don't share a state, but we need to take the newly created organization id somehow. To do that we ",[249,690,691],{},"declare"," an id at the top of the function which every statement in the function can access too.",[26,694,695,696,699,700,703,704,707,708,711,712,714,715,718],{},"We ",[249,697,698],{},"returning"," the id from the ",[249,701,702],{},"insert"," command ",[249,705,706],{},"into"," the ",[249,709,710],{},"org_id"," variable we declared. Then we can use it in the next ",[249,713,702],{}," statement for our ",[249,716,717],{},"organization_id"," column.",[458,720,722],{"id":721},"_3-trigger","3. Trigger",[26,724,725,726,729,730,735,736,739,740,742],{},"Finally, we somehow need to call this function automatically every time a new row is created in the ",[249,727,728],{},"auth.users"," table, and we can do that with ",[58,731,734],{"href":732,"rel":733},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fdatabase\u002Fpostgres\u002Ftriggers",[62],"triggers",". So let's create a trigger to execute the ",[249,737,738],{},"handle_new_org_and_profile"," function we created above for each row in ",[249,741,728],{}," after the database inserts the row.",[242,744,747],{"className":470,"code":745,"filename":746,"language":473,"meta":188,"style":188},"create trigger on_auth_user_create_org_and_profile\n  after insert on auth.users\n  for each row execute procedure handle_new_org_and_profile();\n","supabseTrigger.sql",[249,748,749,754,759],{"__ignoreMap":188},[252,750,751],{"class":254,"line":255},[252,752,753],{},"create trigger on_auth_user_create_org_and_profile\n",[252,755,756],{"class":254,"line":189},[252,757,758],{},"  after insert on auth.users\n",[252,760,761],{"class":254,"line":326},[252,762,763],{},"  for each row execute procedure handle_new_org_and_profile();\n",[458,765,767],{"id":766},"_4-summary","4. Summary",[26,769,770],{},"You're now all set.",[772,773,774],"style",{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":188,"searchDepth":189,"depth":189,"links":776},[777,778,779,780],{"id":460,"depth":189,"text":461},{"id":580,"depth":189,"text":581},{"id":721,"depth":189,"text":722},{"id":766,"depth":189,"text":767},"2024-02-11","Using a Supabase Postgres trigger to auto-create a profile on signup.",{"src":784},"\u002Fposts\u002F2024\u002Fsupabase-functions\u002Fcover.webp",{},"\u002Fblog\u002F2024\u002Fsupabase-functions",{"title":205,"description":782},"blog\u002F2024\u002Fsupabase-functions","_nrw-NQpAklXH0zZNqwK7SjUfvEyCpTrUxRFiFqgL98",{"id":791,"title":792,"author":793,"badge":795,"body":797,"date":994,"description":995,"draft":193,"extension":194,"image":996,"meta":998,"navigation":198,"path":999,"seo":1000,"stem":1001,"__hash__":1002},"posts\u002Fblog\u002F2022\u002Fgithub-email.md","Change your email in GitHub",{"name":16,"to":17,"avatar":794},{"src":19},{"label":796},"tutorials",{"type":23,"value":798,"toc":987},[799,802,806,815,818,821,825,839,848,852,855,862,865,884,920,934,938,947,958,965,969,984],[26,800,801],{},"Once in a lifetime a man changes his email address. Maybe you didn't change your email even once, I completely understand you, but I did. And let me tell you - it's not an easy process!",[458,803,805],{"id":804},"_1-why-did-i-do-it","1. Why did I do it?",[26,807,808,809,814],{},"I have a private domain and I used it with Gmail to have a private email address. But Google doesn't give you the option to set up a private domain because they'll lose a lot of small businesses that use ",[58,810,813],{"href":811,"rel":812},"https:\u002F\u002Fworkspace.google.com",[62],"Google workspace",". So I set up a \"small business\" with the domain and paid Google a few dollars every month.",[26,816,817],{},"But then I got married and set up my wife her own custom email address and added a few more dollars to Google every month. And after couple of years, my first child was born, and I thought to myself - when I set up their email in the future I'll also have to pay for their user every month. And I started to realize that in a few years I'll set myself up to pay Google hundreds of dollars every year for an email address (I also pay a yearly fee for the domain).",[26,819,820],{},"But it's 2022 - an email address is basically free nowadays. It doesn't make sense to me. And then Apple announced iOS 15, and one of the features was \"Custom Email Domain with iCloud Mail\"! And I already subscribed to iCloud+, and use iPhone and Mac, so it was a no brainer!",[458,822,824],{"id":823},"_2-apple","2. Apple",[26,826,827,828,120,833,838],{},"I was a Mac and iPhone user and I used Google services, but iOS 15 and iOS 16 changed this. Apple caught up to most (if not all) the major features in Mail, Calendar, and Drive (they have a few more in Photos, and they're already better in Reminders IMO). But they even took a few more steps ahead with ",[58,829,832],{"href":830,"rel":831},"https:\u002F\u002Fsupport.apple.com\u002Fen-om\u002Fguide\u002Ficloud\u002Fmm9d9012c9e8\u002Ficloud",[62],"Hide my Email",[58,834,837],{"href":835,"rel":836},"https:\u002F\u002Fsupport.apple.com\u002Fen-us\u002FHT212614",[62],"Private Relay",". So it was time to move all of my life (Mail, Calendar, Drive, and Photos) to Apple, and to make this process a little easier for me I started with a fresh email address (so I'll not lose the old account for a little longer).",[26,840,841,842,847],{},"The first thing I did is to go over my ",[58,843,846],{"href":844,"rel":845},"https:\u002F\u002F1password.com",[62],"1Password"," login list and enter to all the services and change their email address (or delete the account if I didn't actually use the service, so it was actually a good time to clean things. You'll be surprised how many services you're registered on over the years).",[458,849,851],{"id":850},"_3-github","3. GitHub",[26,853,854],{},"Everything was going according to plan, but little did I know that when you change your email on GitHub you lose all your commits that were made with the old email - they're no longer counted in your contributions heat map!",[26,856,857],{},[41,858],{"alt":859,"className":860,"src":861},"GitHub Contributions Graph",[45,46],"\u002Fposts\u002F2022\u002Fgithub-email\u002Fgithub-contributions-graph.webp",[26,863,864],{},"So I wrote a command line script to change the commits author email. But before we continue I do have to warn you about a few things",[866,867,868,874,881],"ol",{},[154,869,870,126],{},[871,872,873],"strong",{},"This action changes some of the history commit's fields. Be very careful about how you run it and on which repositories",[154,875,876,877,880],{},"Note that you'll need to have full access to the ",[249,878,879],{},"main"," branch to run this script.",[154,882,883],{},"Be aware that the commits that do change will change their sha, so if you have tags that link to those sha's - they'll no longer link to the right commit (you can change the tag sha by creating it again, and you can set the same date when you create it).",[242,885,890],{"className":886,"code":887,"filename":888,"language":889,"meta":188,"style":188},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","git filter-branch -f --commit-filter 'if [ \"$GIT_AUTHOR_EMAIL\" = \"\u003COLD_EMAIL_ADDRESS>\" ]; then GIT_AUTHOR_EMAIL=\"\u003CNEW_EMAIL_ADDRESS>\"; git commit-tree \"$@\"; fi' HEAD\n","terminal","bash",[249,891,892],{"__ignoreMap":188},[252,893,894,898,902,905,908,911,914,917],{"class":254,"line":255},[252,895,897],{"class":896},"sBMFI","git",[252,899,901],{"class":900},"sfazB"," filter-branch",[252,903,904],{"class":900}," -f",[252,906,907],{"class":900}," --commit-filter",[252,909,910],{"class":262}," '",[252,912,913],{"class":900},"if [ \"$GIT_AUTHOR_EMAIL\" = \"\u003COLD_EMAIL_ADDRESS>\" ]; then GIT_AUTHOR_EMAIL=\"\u003CNEW_EMAIL_ADDRESS>\"; git commit-tree \"$@\"; fi",[252,915,916],{"class":262},"'",[252,918,919],{"class":900}," HEAD\n",[26,921,922,923,120,926,929,930,933],{},"All you have to do is to replace ",[249,924,925],{},"\u003COLD_EMAIL_ADDRESS>",[249,927,928],{},"\u003CNEW_EMAIL_ADDRESS>"," with your old email address and new email address respectively, and run this bash command in every one of the repositories you want to change your email address in (and then ",[249,931,932],{},"git push --force origin main"," to change the commit history in GitHub and not just on your local git).",[458,935,937],{"id":936},"_4-github-private-email-addresses","4. GitHub private email addresses",[26,939,940,941,946],{},"There is one thing I'll recommend GitHub users do to not rely on their email address in their commits, and that's using ",[58,942,945],{"href":943,"rel":944},"https:\u002F\u002Fdocs.github.com\u002Fen\u002Faccount-and-profile\u002Fsetting-up-and-managing-your-personal-account-on-github\u002Fmanaging-email-preferences\u002Fsetting-your-commit-email-address",[62],"GitHub private email address"," (it'll also keep your email private from GitHub emails to others and from people who search your repos commit history to find your email address).",[26,948,949,950,954,955,957],{},"So, GitHub has a feature called ",[951,952,953],"em",{},"\"Keep my email address private\""," where they let you use a noreply email address from GitHub as your commit email address. To use your noreply email address for commits you push from the command line, use that email address when you set your commit email address in Git. To use your noreply address for web-based Git operations just set your commit email address on GitHub and choose ",[951,956,953],{}," in the settings (screenshot below).",[26,959,960],{},[41,961],{"alt":962,"className":963,"src":964},"GitHub Private Email Settings",[45,46],"\u002Fposts\u002F2022\u002Fgithub-email\u002Fgithub-private-email.webp",[458,966,968],{"id":967},"_5-summary","5. Summary",[26,970,971,972,974,975,978,979,120,981,983],{},"So I would recommend everybody to use GitHub's ",[951,973,953],{}," feature, and if you want to change your old commits email to your new ",[249,976,977],{},"noreply"," email address from GitHub - use the bash command I created (don't forget to replace ",[249,980,925],{},[249,982,928],{},").",[772,985,986],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":188,"searchDepth":189,"depth":189,"links":988},[989,990,991,992,993],{"id":804,"depth":189,"text":805},{"id":823,"depth":189,"text":824},{"id":850,"depth":189,"text":851},{"id":936,"depth":189,"text":937},{"id":967,"depth":189,"text":968},"2022-10-01","Migrating email addresses without losing your GitHub contribution history.",{"src":997},"\u002Fposts\u002F2022\u002Fgithub-email\u002Fgoogle-email.webp",{},"\u002Fblog\u002F2022\u002Fgithub-email",{"title":792,"description":995},"blog\u002F2022\u002Fgithub-email","79DXAX_NyMTumetSgbDHEc_5Z7dB-Zp6F9wcD613Elg",{"id":1004,"title":1005,"author":1006,"badge":1008,"body":1009,"date":4513,"description":4514,"draft":193,"extension":194,"image":4515,"meta":4517,"navigation":198,"path":4518,"seo":4519,"stem":4520,"__hash__":4521},"posts\u002Fblog\u002F2022\u002Fdesign-system.md","A Practical Guide to Design System",{"name":16,"to":17,"avatar":1007},{"src":19},{"label":209},{"type":23,"value":1010,"toc":4505},[1011,1014,1049,1057,1061,1068,1084,1087,1094,1108,1115,1133,1136,1151,1157,1164,1199,1210,1317,1328,1363,1381,1390,1406,1409,1663,1669,1817,1821,1824,1860,1880,1903,2113,2119,2504,2510,2517,2521,2544,2547,3019,3029,3215,3221,3252,3272,3283,3307,3322,3477,3481,3492,3495,3507,3510,3524,4115,4129,4188,4191,4198,4212,4216,4225,4228,4237,4256,4263,4275,4442,4460,4471,4478,4485,4492,4496,4499,4502],[26,1012,1013],{},"It's no secret that design systems are taking over the web development world and that's a good thing IMO. But nonetheless, it's still a challenge to set up one. I myself encountered it when I created our own design system in the startup I work for, so I thought I'll save others some time and document my process.",[26,1015,1016,1017,1022,1023,1026,1027,1030,1031,1036,1037,1042,1043,1048],{},"In my startup we use ",[58,1018,1021],{"href":1019,"rel":1020},"https:\u002F\u002Fvuejs.org",[62],"Vue.js"," (version 3, with ",[249,1024,1025],{},"composition"," api and ",[249,1028,1029],{},"sfc"," style), so naturally we also use ",[58,1032,1035],{"href":1033,"rel":1034},"https:\u002F\u002Fvitejs.dev",[62],"Vite",", and we use ",[58,1038,1041],{"href":1039,"rel":1040},"https:\u002F\u002Ftailwindcss.com",[62],"Tailwind CSS"," so I'll go over it also in the setup process. The UI we choose to build the components with is ",[58,1044,1047],{"href":1045,"rel":1046},"https:\u002F\u002Fstorybook.js.org",[62],"Storybook",", it supports all the frameworks and has the biggest community adoption so it was an easy choice.",[26,1050,1051,1052,126],{},"But first, what is a \"Design System\"? It's a little different if you ask a designer or a developer, but for us developers it's just a component library. It helps us standardize design language, maintain consistency across multiple different products or platforms, document our components, etc. If you want to read more about the architecture and methodology of design systems I recommend reading ",[58,1053,1056],{"href":1054,"rel":1055},"https:\u002F\u002Fatomicdesign.bradfrost.com\u002Fchapter-2",[62],"Atomic Design Methodology by Brad Frost",[458,1058,1060],{"id":1059},"_1-scaffolding-the-project","1. Scaffolding the project",[26,1062,1063,1064,1067],{},"The first thing we need to do is actually create a regular ",[58,1065,1021],{"href":1019,"rel":1066},[62]," project",[242,1069,1071],{"className":886,"code":1070,"filename":888,"language":889,"meta":188,"style":188},"npm init vue@latest\n",[249,1072,1073],{"__ignoreMap":188},[252,1074,1075,1078,1081],{"class":254,"line":255},[252,1076,1077],{"class":896},"npm",[252,1079,1080],{"class":900}," init",[252,1082,1083],{"class":900}," vue@latest\n",[26,1085,1086],{},"You'll be presented with prompts for a number of optional features, choose whatever you want, it's not important for the rest of the tutorial. But, if you want to see my choices - they're in the screenshot below.",[26,1088,1089],{},[41,1090],{"alt":1091,"className":1092,"src":1093},"Vuejs CLI setup",[45,46],"\u002Fposts\u002F2022\u002Fdesign-system\u002Fvue-cli-setup.webp",[26,1095,1096,1097,1100,1101,1104,1105,983],{},"After we have an empty vue.js project (I called mine ",[249,1098,1099],{},"daisy"," because Daisy the Design System sounds nice in my head) we can ",[249,1102,1103],{},"cd"," into it and install all the dependencies (",[249,1106,1107],{},"npm install",[26,1109,1110,1111,1114],{},"The next step is to install ",[58,1112,1047],{"href":1045,"rel":1113},[62],". The current version is 6.x, but I choose to install the beta (7.x) version because it's close enough to release and there are a lot of changes that integrate well with our setup (like vite and typescript support out of the box).",[242,1116,1118],{"className":886,"code":1117,"filename":888,"language":889,"meta":188,"style":188},"npx sb@next init --builder=vite\n",[249,1119,1120],{"__ignoreMap":188},[252,1121,1122,1125,1128,1130],{"class":254,"line":255},[252,1123,1124],{"class":896},"npx",[252,1126,1127],{"class":900}," sb@next",[252,1129,1080],{"class":900},[252,1131,1132],{"class":900}," --builder=vite\n",[26,1134,1135],{},"And once the Storybook CLI is done running and doing its thing, we can run it and see our storybook default components and documentation in action",[242,1137,1139],{"className":886,"code":1138,"filename":888,"language":889,"meta":188,"style":188},"npm run storybook\n",[249,1140,1141],{"__ignoreMap":188},[252,1142,1143,1145,1148],{"class":254,"line":255},[252,1144,1077],{"class":896},[252,1146,1147],{"class":900}," run",[252,1149,1150],{"class":900}," storybook\n",[26,1152,1153],{},[41,1154],{"alt":1047,"className":1155,"src":1156},[45,46],"\u002Fposts\u002F2022\u002Fdesign-system\u002Fstorybook.webp",[26,1158,1159,1160,1163],{},"The last thing we need to do is to add ",[58,1161,1041],{"href":1039,"rel":1162},[62]," support to our Vue.js components and as well as our Storybook UI. Adding it to the vuejs project is really easy",[242,1165,1167],{"className":886,"code":1166,"filename":888,"language":889,"meta":188,"style":188},"npm install -D tailwindcss postcss autoprefixer\nnpx tailwindcss init -p\n",[249,1168,1169,1188],{"__ignoreMap":188},[252,1170,1171,1173,1176,1179,1182,1185],{"class":254,"line":255},[252,1172,1077],{"class":896},[252,1174,1175],{"class":900}," install",[252,1177,1178],{"class":900}," -D",[252,1180,1181],{"class":900}," tailwindcss",[252,1183,1184],{"class":900}," postcss",[252,1186,1187],{"class":900}," autoprefixer\n",[252,1189,1190,1192,1194,1196],{"class":254,"line":189},[252,1191,1124],{"class":896},[252,1193,1181],{"class":900},[252,1195,1080],{"class":900},[252,1197,1198],{"class":900}," -p\n",[26,1200,1201,1202,1205,1206,1209],{},"And all you have left to do is to edit your ",[249,1203,1204],{},"content"," section (in ",[249,1207,1208],{},"tailwind.config.js"," file) to include the vuejs files",[242,1211,1217],{"className":1212,"code":1213,"filename":1214,"highlights":1215,"language":1216,"meta":188,"style":188},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F** @type {import('tailwindcss').Config} *\u002F\nmodule.exports = {\n  content: [\".\u002Fsrc\u002F**\u002F*.{vue,js,ts,jsx,tsx}\"],\n  theme: {\n    extend: {},\n  },\n  plugins: [],\n};\n","tailwind.js",[326],"js",[249,1218,1219,1243,1252,1277,1286,1296,1300,1312],{"__ignoreMap":188},[252,1220,1221,1225,1228,1232,1234,1238,1240],{"class":254,"line":255},[252,1222,1224],{"class":1223},"sHwdD","\u002F** ",[252,1226,1227],{"class":282},"@",[252,1229,1231],{"class":1230},"s6hCs","type",[252,1233,263],{"class":282},[252,1235,1237],{"class":1236},"sFweD","import('tailwindcss').Config",[252,1239,276],{"class":282},[252,1241,1242],{"class":1223}," *\u002F\n",[252,1244,1245,1248,1250],{"class":254,"line":189},[252,1246,1247],{"class":262},"module.exports",[252,1249,279],{"class":262},[252,1251,351],{"class":262},[252,1253,1256,1259,1261,1264,1267,1270,1272,1275],{"class":1254,"line":326},[254,1255],"highlight",[252,1257,1258],{"class":308},"  content",[252,1260,312],{"class":262},[252,1262,1263],{"class":266}," [",[252,1265,1266],{"class":262},"\"",[252,1268,1269],{"class":900},".\u002Fsrc\u002F**\u002F*.{vue,js,ts,jsx,tsx}",[252,1271,1266],{"class":262},[252,1273,1274],{"class":266},"]",[252,1276,323],{"class":262},[252,1278,1279,1282,1284],{"class":254,"line":343},[252,1280,1281],{"class":308},"  theme",[252,1283,312],{"class":262},[252,1285,351],{"class":262},[252,1287,1288,1291,1293],{"class":254,"line":354},[252,1289,1290],{"class":308},"    extend",[252,1292,312],{"class":262},[252,1294,1295],{"class":262}," {},\n",[252,1297,1298],{"class":254,"line":364},[252,1299,424],{"class":262},[252,1301,1302,1305,1307,1310],{"class":254,"line":381},[252,1303,1304],{"class":308},"  plugins",[252,1306,312],{"class":262},[252,1308,1309],{"class":266}," []",[252,1311,323],{"class":262},[252,1313,1314],{"class":254,"line":398},[252,1315,1316],{"class":262},"};\n",[26,1318,1319,1320,1323,1324,1327],{},"Lastly, include the Tailwind directives to our CSS by creating a new ",[249,1321,1322],{},"style.css"," file inside the ",[249,1325,1326],{},"src"," directory and add to it the @tailwind directives:",[242,1329,1333],{"className":1330,"code":1331,"filename":1322,"language":1332,"meta":188,"style":188},"language-css shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","@tailwind base;\n@tailwind components;\n@tailwind utilities;\n","css",[249,1334,1335,1345,1354],{"__ignoreMap":188},[252,1336,1337,1340,1343],{"class":254,"line":255},[252,1338,1339],{"class":282},"@tailwind",[252,1341,1342],{"class":266}," base",[252,1344,435],{"class":262},[252,1346,1347,1349,1352],{"class":254,"line":189},[252,1348,1339],{"class":282},[252,1350,1351],{"class":266}," components",[252,1353,435],{"class":262},[252,1355,1356,1358,1361],{"class":254,"line":326},[252,1357,1339],{"class":282},[252,1359,1360],{"class":266}," utilities",[252,1362,435],{"class":262},[26,1364,1365,1366,1369,1370,1373,1374,120,1377,1380],{},"Now if you launch your Vue.js app (",[249,1367,1368],{},"npm run dev",") Tailwind class will work, but if you launch your Storybook (",[249,1371,1372],{},"npm run storybook",") they'll not. That's because Storybook uses its own Vite config to run the Vue.js app (as you can see, you don't run the ",[249,1375,1376],{},"index.html",[249,1378,1379],{},"App.vue"," files, so you can actually delete them - that's what I did).",[26,1382,1383,1384,1389],{},"To fix this we'll use a ",[58,1385,1388],{"href":1386,"rel":1387},"https:\u002F\u002Fstorybook.js.org\u002Faddons\u002F@storybook\u002Faddon-postcss",[62],"Storybook addon"," to run the PostCSS preprocessor against our stories",[242,1391,1393],{"className":886,"code":1392,"filename":888,"language":889,"meta":188,"style":188},"npm install -D @storybook\u002Faddon-postcss\n",[249,1394,1395],{"__ignoreMap":188},[252,1396,1397,1399,1401,1403],{"class":254,"line":255},[252,1398,1077],{"class":896},[252,1400,1175],{"class":900},[252,1402,1178],{"class":900},[252,1404,1405],{"class":900}," @storybook\u002Faddon-postcss\n",[26,1407,1408],{},"then add it",[242,1410,1415],{"className":1212,"code":1411,"filename":1412,"highlights":1413,"language":1216,"meta":188,"style":188},"const path = require(\"path\");\nmodule.exports = {\n  stories: [\"..\u002Fsrc\u002F**\u002F*.stories.mdx\", \"..\u002Fsrc\u002F**\u002F*.stories.@(js|jsx|ts|tsx)\"],\n  addons: [\n    \"@storybook\u002Faddon-links\",\n    \"@storybook\u002Faddon-essentials\",\n    \"@storybook\u002Faddon-interactions\",\n    {\n      name: \"@storybook\u002Faddon-postcss\",\n      options: {\n        postcssLoaderOptions: {\n          implementation: require(\"postcss\"),\n        },\n      },\n    },\n  ],\n  framework: {\n    name: \"@storybook\u002Fvue3-vite\",\n    options: {},\n  },\n};\n",".storybook\u002Fmain.js",[398,415,421,427,662,668,674,1414],15,[249,1416,1417,1443,1451,1481,1491,1503,1514,1525,1531,1548,1558,1568,1591,1597,1603,1608,1616,1626,1643,1653,1658],{"__ignoreMap":188},[252,1418,1419,1421,1424,1427,1430,1432,1434,1437,1439,1441],{"class":254,"line":255},[252,1420,259],{"class":258},[252,1422,1423],{"class":266}," path ",[252,1425,1426],{"class":262},"=",[252,1428,1429],{"class":296}," require",[252,1431,300],{"class":266},[252,1433,1266],{"class":262},[252,1435,1436],{"class":900},"path",[252,1438,1266],{"class":262},[252,1440,432],{"class":266},[252,1442,435],{"class":262},[252,1444,1445,1447,1449],{"class":254,"line":189},[252,1446,1247],{"class":262},[252,1448,279],{"class":262},[252,1450,351],{"class":262},[252,1452,1453,1456,1458,1460,1462,1465,1467,1469,1472,1475,1477,1479],{"class":254,"line":326},[252,1454,1455],{"class":308},"  stories",[252,1457,312],{"class":262},[252,1459,1263],{"class":266},[252,1461,1266],{"class":262},[252,1463,1464],{"class":900},"..\u002Fsrc\u002F**\u002F*.stories.mdx",[252,1466,1266],{"class":262},[252,1468,270],{"class":262},[252,1470,1471],{"class":262}," \"",[252,1473,1474],{"class":900},"..\u002Fsrc\u002F**\u002F*.stories.@(js|jsx|ts|tsx)",[252,1476,1266],{"class":262},[252,1478,1274],{"class":266},[252,1480,323],{"class":262},[252,1482,1483,1486,1488],{"class":254,"line":343},[252,1484,1485],{"class":308},"  addons",[252,1487,312],{"class":262},[252,1489,1490],{"class":266}," [\n",[252,1492,1493,1496,1499,1501],{"class":254,"line":354},[252,1494,1495],{"class":262},"    \"",[252,1497,1498],{"class":900},"@storybook\u002Faddon-links",[252,1500,1266],{"class":262},[252,1502,323],{"class":262},[252,1504,1505,1507,1510,1512],{"class":254,"line":364},[252,1506,1495],{"class":262},[252,1508,1509],{"class":900},"@storybook\u002Faddon-essentials",[252,1511,1266],{"class":262},[252,1513,323],{"class":262},[252,1515,1516,1518,1521,1523],{"class":254,"line":381},[252,1517,1495],{"class":262},[252,1519,1520],{"class":900},"@storybook\u002Faddon-interactions",[252,1522,1266],{"class":262},[252,1524,323],{"class":262},[252,1526,1528],{"class":1527,"line":398},[254,1255],[252,1529,1530],{"class":262},"    {\n",[252,1532,1534,1537,1539,1541,1544,1546],{"class":1533,"line":415},[254,1255],[252,1535,1536],{"class":308},"      name",[252,1538,312],{"class":262},[252,1540,1471],{"class":262},[252,1542,1543],{"class":900},"@storybook\u002Faddon-postcss",[252,1545,1266],{"class":262},[252,1547,323],{"class":262},[252,1549,1551,1554,1556],{"class":1550,"line":421},[254,1255],[252,1552,1553],{"class":308},"      options",[252,1555,312],{"class":262},[252,1557,351],{"class":262},[252,1559,1561,1564,1566],{"class":1560,"line":427},[254,1255],[252,1562,1563],{"class":308},"        postcssLoaderOptions",[252,1565,312],{"class":262},[252,1567,351],{"class":262},[252,1569,1571,1574,1576,1578,1580,1582,1585,1587,1589],{"class":1570,"line":662},[254,1255],[252,1572,1573],{"class":308},"          implementation",[252,1575,312],{"class":262},[252,1577,1429],{"class":296},[252,1579,300],{"class":266},[252,1581,1266],{"class":262},[252,1583,1584],{"class":900},"postcss",[252,1586,1266],{"class":262},[252,1588,432],{"class":266},[252,1590,323],{"class":262},[252,1592,1594],{"class":1593,"line":668},[254,1255],[252,1595,1596],{"class":262},"        },\n",[252,1598,1600],{"class":1599,"line":674},[254,1255],[252,1601,1602],{"class":262},"      },\n",[252,1604,1606],{"class":1605,"line":1414},[254,1255],[252,1607,418],{"class":262},[252,1609,1611,1614],{"class":254,"line":1610},16,[252,1612,1613],{"class":266},"  ]",[252,1615,323],{"class":262},[252,1617,1619,1622,1624],{"class":254,"line":1618},17,[252,1620,1621],{"class":308},"  framework",[252,1623,312],{"class":262},[252,1625,351],{"class":262},[252,1627,1629,1632,1634,1636,1639,1641],{"class":254,"line":1628},18,[252,1630,1631],{"class":308},"    name",[252,1633,312],{"class":262},[252,1635,1471],{"class":262},[252,1637,1638],{"class":900},"@storybook\u002Fvue3-vite",[252,1640,1266],{"class":262},[252,1642,323],{"class":262},[252,1644,1646,1649,1651],{"class":254,"line":1645},19,[252,1647,1648],{"class":308},"    options",[252,1650,312],{"class":262},[252,1652,1295],{"class":262},[252,1654,1656],{"class":254,"line":1655},20,[252,1657,424],{"class":262},[252,1659,1661],{"class":254,"line":1660},21,[252,1662,1316],{"class":262},[26,1664,1665,1666,1668],{},"and also import the ",[249,1667,1322],{}," file",[242,1670,1674],{"className":1212,"code":1671,"filename":1672,"highlights":1673,"language":1216,"meta":188,"style":188},"import \"..\u002Fsrc\u002Fstyle.css\";\n\nexport const parameters = {\n  actions: { argTypesRegex: \"^on[A-Z].*\" },\n  controls: {\n    matchers: {\n      color: \u002F(background|color)$\u002Fi,\n      date: \u002FDate$\u002F,\n    },\n  },\n};\n",".storybook\u002Fpreview.js",[255],[249,1675,1676,1691,1696,1711,1735,1744,1753,1786,1805,1809,1813],{"__ignoreMap":188},[252,1677,1679,1682,1684,1687,1689],{"class":1678,"line":255},[254,1255],[252,1680,1681],{"class":282},"import",[252,1683,1471],{"class":262},[252,1685,1686],{"class":900},"..\u002Fsrc\u002Fstyle.css",[252,1688,1266],{"class":262},[252,1690,435],{"class":262},[252,1692,1693],{"class":254,"line":189},[252,1694,1695],{"emptyLinePlaceholder":198},"\n",[252,1697,1698,1701,1704,1707,1709],{"class":254,"line":326},[252,1699,1700],{"class":282},"export",[252,1702,1703],{"class":258}," const",[252,1705,1706],{"class":266}," parameters ",[252,1708,1426],{"class":262},[252,1710,351],{"class":262},[252,1712,1713,1716,1718,1720,1723,1725,1727,1730,1732],{"class":254,"line":343},[252,1714,1715],{"class":308},"  actions",[252,1717,312],{"class":262},[252,1719,263],{"class":262},[252,1721,1722],{"class":308}," argTypesRegex",[252,1724,312],{"class":262},[252,1726,1471],{"class":262},[252,1728,1729],{"class":900},"^on[A-Z].*",[252,1731,1266],{"class":262},[252,1733,1734],{"class":262}," },\n",[252,1736,1737,1740,1742],{"class":254,"line":354},[252,1738,1739],{"class":308},"  controls",[252,1741,312],{"class":262},[252,1743,351],{"class":262},[252,1745,1746,1749,1751],{"class":254,"line":364},[252,1747,1748],{"class":308},"    matchers",[252,1750,312],{"class":262},[252,1752,351],{"class":262},[252,1754,1755,1758,1760,1763,1766,1769,1772,1774,1777,1780,1784],{"class":254,"line":381},[252,1756,1757],{"class":308},"      color",[252,1759,312],{"class":262},[252,1761,1762],{"class":262}," \u002F(",[252,1764,1765],{"class":900},"background",[252,1767,1768],{"class":262},"|",[252,1770,1771],{"class":900},"color",[252,1773,432],{"class":262},[252,1775,1776],{"class":282},"$",[252,1778,1779],{"class":262},"\u002F",[252,1781,1783],{"class":1782},"sbssI","i",[252,1785,323],{"class":262},[252,1787,1788,1791,1793,1796,1799,1801,1803],{"class":254,"line":398},[252,1789,1790],{"class":308},"      date",[252,1792,312],{"class":262},[252,1794,1795],{"class":262}," \u002F",[252,1797,1798],{"class":900},"Date",[252,1800,1776],{"class":282},[252,1802,1779],{"class":262},[252,1804,323],{"class":262},[252,1806,1807],{"class":254,"line":415},[252,1808,418],{"class":262},[252,1810,1811],{"class":254,"line":421},[252,1812,424],{"class":262},[252,1814,1815],{"class":254,"line":427},[252,1816,1316],{"class":262},[458,1818,1820],{"id":1819},"_2-build-our-first-component","2. Build our first component",[26,1822,1823],{},"Building our first component is just like building a regular Vue.js component, except we'll add it a stories file.",[26,1825,1826,1827,1830,1831,1834,1835,1838,1839,105,1844,1847,1848,1851,1852,1855,1856,1859],{},"I'll call my component ",[249,1828,1829],{},"Foo"," and create a directory with that name in the ",[249,1832,1833],{},"components"," directory (I'll actually call it ",[249,1836,1837],{},"DyFoo"," because ",[58,1840,1843],{"href":1841,"rel":1842},"https:\u002F\u002Feslint.vuejs.org\u002Frules\u002Fmulti-word-component-names.html",[62],"ESlint requires component names to always be multi-word",[249,1845,1846],{},"D"," is the first letter of our Design System name, and ",[249,1849,1850],{},"Y"," is the last), and I'll also create a ",[249,1853,1854],{},".vue"," and a ",[249,1857,1858],{},".stories.js"," files with the same component name.",[242,1861,1863],{"className":886,"code":1862,"filename":888,"language":889,"meta":188,"style":188},"touch src\u002Fcomponents\u002FDyFoo\u002FDyFoo.vue\ntouch src\u002Fcomponents\u002FDyFoo\u002FDyFoo.stories.js\n",[249,1864,1865,1873],{"__ignoreMap":188},[252,1866,1867,1870],{"class":254,"line":255},[252,1868,1869],{"class":896},"touch",[252,1871,1872],{"class":900}," src\u002Fcomponents\u002FDyFoo\u002FDyFoo.vue\n",[252,1874,1875,1877],{"class":254,"line":189},[252,1876,1869],{"class":896},[252,1878,1879],{"class":900}," src\u002Fcomponents\u002FDyFoo\u002FDyFoo.stories.js\n",[26,1881,1882,1883,1886,1887,1890,1891,1894,1895,1898,1899,1902],{},"My ",[249,1884,1885],{},"DyFoo.vue"," component will be fairly easy, it'll take ",[249,1888,1889],{},"variant"," prop with ",[249,1892,1893],{},"primary"," or ",[249,1896,1897],{},"secondary"," options and give the text ",[249,1900,1901],{},"foo"," some color based on them.",[242,1904,1908],{"className":1905,"code":1906,"filename":1885,"language":1907,"meta":188,"style":188},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript setup lang=\"ts\">\ndefineProps({\n  variant: {\n    type: String,\n    default: \"primary\",\n    validator(value: string) {\n      return [\"primary\", \"secondary\"].includes(value);\n    },\n  },\n});\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp :class=\"[variant == 'primary' ? 'text-sky-500' : '', variant == 'secondary' ? 'text-gray-500' : '']\">foo\u003C\u002Fp>\n\u003C\u002Ftemplate>\n","vue",[249,1909,1910,1936,1945,1954,1966,1981,2001,2037,2041,2045,2053,2062,2066,2075,2105],{"__ignoreMap":188},[252,1911,1912,1915,1918,1921,1924,1926,1928,1931,1933],{"class":254,"line":255},[252,1913,1914],{"class":262},"\u003C",[252,1916,1917],{"class":308},"script",[252,1919,1920],{"class":258}," setup",[252,1922,1923],{"class":258}," lang",[252,1925,1426],{"class":262},[252,1927,1266],{"class":262},[252,1929,1930],{"class":900},"ts",[252,1932,1266],{"class":262},[252,1934,1935],{"class":262},">\n",[252,1937,1938,1941,1943],{"class":254,"line":189},[252,1939,1940],{"class":296},"defineProps",[252,1942,300],{"class":266},[252,1944,303],{"class":262},[252,1946,1947,1950,1952],{"class":254,"line":326},[252,1948,1949],{"class":308},"  variant",[252,1951,312],{"class":262},[252,1953,351],{"class":262},[252,1955,1956,1959,1961,1964],{"class":254,"line":343},[252,1957,1958],{"class":308},"    type",[252,1960,312],{"class":262},[252,1962,1963],{"class":266}," String",[252,1965,323],{"class":262},[252,1967,1968,1971,1973,1975,1977,1979],{"class":254,"line":354},[252,1969,1970],{"class":308},"    default",[252,1972,312],{"class":262},[252,1974,1471],{"class":262},[252,1976,1893],{"class":900},[252,1978,1266],{"class":262},[252,1980,323],{"class":262},[252,1982,1983,1986,1988,1992,1994,1997,1999],{"class":254,"line":364},[252,1984,1985],{"class":308},"    validator",[252,1987,300],{"class":262},[252,1989,1991],{"class":1990},"sHdIc","value",[252,1993,312],{"class":262},[252,1995,1996],{"class":896}," string",[252,1998,432],{"class":262},[252,2000,351],{"class":262},[252,2002,2003,2006,2008,2010,2012,2014,2016,2018,2020,2022,2024,2026,2029,2031,2033,2035],{"class":254,"line":381},[252,2004,2005],{"class":282},"      return",[252,2007,1263],{"class":308},[252,2009,1266],{"class":262},[252,2011,1893],{"class":900},[252,2013,1266],{"class":262},[252,2015,270],{"class":262},[252,2017,1471],{"class":262},[252,2019,1897],{"class":900},[252,2021,1266],{"class":262},[252,2023,1274],{"class":308},[252,2025,126],{"class":262},[252,2027,2028],{"class":296},"includes",[252,2030,300],{"class":308},[252,2032,1991],{"class":266},[252,2034,432],{"class":308},[252,2036,435],{"class":262},[252,2038,2039],{"class":254,"line":398},[252,2040,418],{"class":262},[252,2042,2043],{"class":254,"line":415},[252,2044,424],{"class":262},[252,2046,2047,2049,2051],{"class":254,"line":421},[252,2048,276],{"class":262},[252,2050,432],{"class":266},[252,2052,435],{"class":262},[252,2054,2055,2058,2060],{"class":254,"line":427},[252,2056,2057],{"class":262},"\u003C\u002F",[252,2059,1917],{"class":308},[252,2061,1935],{"class":262},[252,2063,2064],{"class":254,"line":662},[252,2065,1695],{"emptyLinePlaceholder":198},[252,2067,2068,2070,2073],{"class":254,"line":668},[252,2069,1914],{"class":262},[252,2071,2072],{"class":308},"template",[252,2074,1935],{"class":262},[252,2076,2077,2080,2082,2085,2087,2089,2092,2094,2097,2099,2101,2103],{"class":254,"line":674},[252,2078,2079],{"class":262},"  \u003C",[252,2081,26],{"class":308},[252,2083,2084],{"class":258}," :class",[252,2086,1426],{"class":262},[252,2088,1266],{"class":262},[252,2090,2091],{"class":900},"[variant == 'primary' ? 'text-sky-500' : '', variant == 'secondary' ? 'text-gray-500' : '']",[252,2093,1266],{"class":262},[252,2095,2096],{"class":262},">",[252,2098,1901],{"class":266},[252,2100,2057],{"class":262},[252,2102,26],{"class":308},[252,2104,1935],{"class":262},[252,2106,2107,2109,2111],{"class":254,"line":1414},[252,2108,2057],{"class":262},[252,2110,2072],{"class":308},[252,2112,1935],{"class":262},[26,2114,2115,2116,2118],{},"Now let's create couple of stories to demo those 2 options for the ",[249,2117,1837],{}," component",[242,2120,2123],{"className":1212,"code":2121,"filename":2122,"language":1216,"meta":188,"style":188},"import DyFoo from \".\u002FDyFoo.vue\";\n\nexport default {\n  title: \"Example\u002FFoo\",\n  component: DyFoo,\n  argTypes: {\n    variant: {\n      control: { type: \"select\" },\n      options: [\"primary\", \"secondary\"],\n    },\n  },\n};\n\nconst Template = (args) => ({\n  components: { DyFoo },\n  setup() {\n    return { args };\n  },\n  template: '\u003CDyFoo v-bind=\"args\" \u002F>',\n});\n\nexport const Primary = Template.bind({});\nPrimary.args = {\n  primary: true,\n  variant: \"primary\",\n};\n\nexport const Secondary = Template.bind({});\nSecondary.args = {\n  variant: \"secondary\",\n};\n","DyFoo.stories.js",[249,2124,2125,2144,2148,2157,2173,2185,2194,2203,2226,2252,2256,2260,2264,2268,2292,2306,2316,2329,2333,2349,2357,2361,2390,2405,2419,2434,2439,2444,2470,2484,2499],{"__ignoreMap":188},[252,2126,2127,2129,2132,2135,2137,2140,2142],{"class":254,"line":255},[252,2128,1681],{"class":282},[252,2130,2131],{"class":266}," DyFoo ",[252,2133,2134],{"class":282},"from",[252,2136,1471],{"class":262},[252,2138,2139],{"class":900},".\u002FDyFoo.vue",[252,2141,1266],{"class":262},[252,2143,435],{"class":262},[252,2145,2146],{"class":254,"line":189},[252,2147,1695],{"emptyLinePlaceholder":198},[252,2149,2150,2152,2155],{"class":254,"line":326},[252,2151,1700],{"class":282},[252,2153,2154],{"class":282}," default",[252,2156,351],{"class":262},[252,2158,2159,2162,2164,2166,2169,2171],{"class":254,"line":343},[252,2160,2161],{"class":308},"  title",[252,2163,312],{"class":262},[252,2165,1471],{"class":262},[252,2167,2168],{"class":900},"Example\u002FFoo",[252,2170,1266],{"class":262},[252,2172,323],{"class":262},[252,2174,2175,2178,2180,2183],{"class":254,"line":354},[252,2176,2177],{"class":308},"  component",[252,2179,312],{"class":262},[252,2181,2182],{"class":266}," DyFoo",[252,2184,323],{"class":262},[252,2186,2187,2190,2192],{"class":254,"line":364},[252,2188,2189],{"class":308},"  argTypes",[252,2191,312],{"class":262},[252,2193,351],{"class":262},[252,2195,2196,2199,2201],{"class":254,"line":381},[252,2197,2198],{"class":308},"    variant",[252,2200,312],{"class":262},[252,2202,351],{"class":262},[252,2204,2205,2208,2210,2212,2215,2217,2219,2222,2224],{"class":254,"line":398},[252,2206,2207],{"class":308},"      control",[252,2209,312],{"class":262},[252,2211,263],{"class":262},[252,2213,2214],{"class":308}," type",[252,2216,312],{"class":262},[252,2218,1471],{"class":262},[252,2220,2221],{"class":900},"select",[252,2223,1266],{"class":262},[252,2225,1734],{"class":262},[252,2227,2228,2230,2232,2234,2236,2238,2240,2242,2244,2246,2248,2250],{"class":254,"line":415},[252,2229,1553],{"class":308},[252,2231,312],{"class":262},[252,2233,1263],{"class":266},[252,2235,1266],{"class":262},[252,2237,1893],{"class":900},[252,2239,1266],{"class":262},[252,2241,270],{"class":262},[252,2243,1471],{"class":262},[252,2245,1897],{"class":900},[252,2247,1266],{"class":262},[252,2249,1274],{"class":266},[252,2251,323],{"class":262},[252,2253,2254],{"class":254,"line":421},[252,2255,418],{"class":262},[252,2257,2258],{"class":254,"line":427},[252,2259,424],{"class":262},[252,2261,2262],{"class":254,"line":662},[252,2263,1316],{"class":262},[252,2265,2266],{"class":254,"line":668},[252,2267,1695],{"emptyLinePlaceholder":198},[252,2269,2270,2272,2275,2277,2280,2283,2285,2288,2290],{"class":254,"line":674},[252,2271,259],{"class":258},[252,2273,2274],{"class":266}," Template ",[252,2276,1426],{"class":262},[252,2278,2279],{"class":262}," (",[252,2281,2282],{"class":1990},"args",[252,2284,432],{"class":262},[252,2286,2287],{"class":258}," =>",[252,2289,2279],{"class":266},[252,2291,303],{"class":262},[252,2293,2294,2297,2299,2301,2303],{"class":254,"line":1414},[252,2295,2296],{"class":308},"  components",[252,2298,312],{"class":262},[252,2300,263],{"class":262},[252,2302,2131],{"class":266},[252,2304,2305],{"class":262},"},\n",[252,2307,2308,2311,2314],{"class":254,"line":1610},[252,2309,2310],{"class":308},"  setup",[252,2312,2313],{"class":262},"()",[252,2315,351],{"class":262},[252,2317,2318,2321,2323,2326],{"class":254,"line":1618},[252,2319,2320],{"class":282},"    return",[252,2322,263],{"class":262},[252,2324,2325],{"class":266}," args",[252,2327,2328],{"class":262}," };\n",[252,2330,2331],{"class":254,"line":1628},[252,2332,424],{"class":262},[252,2334,2335,2338,2340,2342,2345,2347],{"class":254,"line":1645},[252,2336,2337],{"class":308},"  template",[252,2339,312],{"class":262},[252,2341,910],{"class":262},[252,2343,2344],{"class":900},"\u003CDyFoo v-bind=\"args\" \u002F>",[252,2346,916],{"class":262},[252,2348,323],{"class":262},[252,2350,2351,2353,2355],{"class":254,"line":1655},[252,2352,276],{"class":262},[252,2354,432],{"class":266},[252,2356,435],{"class":262},[252,2358,2359],{"class":254,"line":1660},[252,2360,1695],{"emptyLinePlaceholder":198},[252,2362,2364,2366,2368,2371,2373,2376,2378,2381,2383,2386,2388],{"class":254,"line":2363},22,[252,2365,1700],{"class":282},[252,2367,1703],{"class":258},[252,2369,2370],{"class":266}," Primary ",[252,2372,1426],{"class":262},[252,2374,2375],{"class":266}," Template",[252,2377,126],{"class":262},[252,2379,2380],{"class":296},"bind",[252,2382,300],{"class":266},[252,2384,2385],{"class":262},"{}",[252,2387,432],{"class":266},[252,2389,435],{"class":262},[252,2391,2393,2396,2398,2401,2403],{"class":254,"line":2392},23,[252,2394,2395],{"class":266},"Primary",[252,2397,126],{"class":262},[252,2399,2400],{"class":266},"args ",[252,2402,1426],{"class":262},[252,2404,351],{"class":262},[252,2406,2408,2411,2413,2417],{"class":254,"line":2407},24,[252,2409,2410],{"class":308},"  primary",[252,2412,312],{"class":262},[252,2414,2416],{"class":2415},"sfNiH"," true",[252,2418,323],{"class":262},[252,2420,2422,2424,2426,2428,2430,2432],{"class":254,"line":2421},25,[252,2423,1949],{"class":308},[252,2425,312],{"class":262},[252,2427,1471],{"class":262},[252,2429,1893],{"class":900},[252,2431,1266],{"class":262},[252,2433,323],{"class":262},[252,2435,2437],{"class":254,"line":2436},26,[252,2438,1316],{"class":262},[252,2440,2442],{"class":254,"line":2441},27,[252,2443,1695],{"emptyLinePlaceholder":198},[252,2445,2447,2449,2451,2454,2456,2458,2460,2462,2464,2466,2468],{"class":254,"line":2446},28,[252,2448,1700],{"class":282},[252,2450,1703],{"class":258},[252,2452,2453],{"class":266}," Secondary ",[252,2455,1426],{"class":262},[252,2457,2375],{"class":266},[252,2459,126],{"class":262},[252,2461,2380],{"class":296},[252,2463,300],{"class":266},[252,2465,2385],{"class":262},[252,2467,432],{"class":266},[252,2469,435],{"class":262},[252,2471,2473,2476,2478,2480,2482],{"class":254,"line":2472},29,[252,2474,2475],{"class":266},"Secondary",[252,2477,126],{"class":262},[252,2479,2400],{"class":266},[252,2481,1426],{"class":262},[252,2483,351],{"class":262},[252,2485,2487,2489,2491,2493,2495,2497],{"class":254,"line":2486},30,[252,2488,1949],{"class":308},[252,2490,312],{"class":262},[252,2492,1471],{"class":262},[252,2494,1897],{"class":900},[252,2496,1266],{"class":262},[252,2498,323],{"class":262},[252,2500,2502],{"class":254,"line":2501},31,[252,2503,1316],{"class":262},[26,2505,2506,2507,2509],{},"If you'll fire Storybook now, you'll see your ",[249,2508,1829],{}," component and you can play with the color of the text by selecting the different options",[26,2511,2512],{},[41,2513],{"alt":2514,"className":2515,"src":2516},"DyFoo component in Storybook",[45,46],"\u002Fposts\u002F2022\u002Fdesign-system\u002FDyFoo.webp",[458,2518,2520],{"id":2519},"_3-bundle-our-library-for-distribution","3. Bundle our library for distribution",[26,2522,2523,2524,2526,2527,2530,2531,2534,2535,2538,2539,126],{},"Now that we have a Vue.js project with Storybook and our first component (",[249,2525,1837],{},"), we want to distribute it. So the first thing we need to do is to tell ",[58,2528,1035],{"href":1033,"rel":2529},[62]," to build a ",[871,2532,2533],{},"component library"," and not a ",[871,2536,2537],{},"project",". And we can do so by setting it up to ",[58,2540,2543],{"href":2541,"rel":2542},"https:\u002F\u002Fvitejs.dev\u002Fguide\u002Fbuild.html#library-mode",[62],"library mode",[26,2545,2546],{},"To package the vite project as a library we need to change some configuration settings",[242,2548,2553],{"className":2549,"code":2550,"filename":2551,"highlights":2552,"language":1930,"meta":188,"style":188},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { resolve } from \"path\";\nimport { fileURLToPath, URL } from \"node:url\";\n\nimport { defineConfig } from \"vite\";\nimport vue from \"@vitejs\u002Fplugin-vue\";\n\n\u002F\u002F https:\u002F\u002Fvitejs.dev\u002Fconfig\u002F\nexport default defineConfig({\n  plugins: [vue()],\n  build: {\n    lib: {\n      entry: resolve(__dirname, \"src\u002Fmain.ts\"),\n      name: \"Daisy\",\n      fileName: (format) => `daisy.${format}.js`,\n      formats: [\"es\", \"cjs\"],\n    },\n    target: \"esnext\",\n    rollupOptions: {\n      \u002F\u002F make sure to externalize deps that shouldn't be bundled into your library\n      external: [\"vue\"],\n      output: {\n        \u002F\u002F Provide global variables to use in the UMD build for externalized deps\n        globals: {\n          vue: \"Vue\",\n        },\n      },\n    },\n  },\n  resolve: {\n    alias: {\n      \"@\": fileURLToPath(new URL(\".\u002Fsrc\", import.meta.url)),\n    },\n  },\n});\n","vite.config.ts",[255,421,427,662,668,674,1414,1610,1618,1628,1645,1655,1660,2363,2392,2407,2421,2436,2441,2446],[249,2554,2555,2579,2606,2610,2632,2650,2654,2659,2671,2686,2696,2706,2732,2748,2786,2816,2821,2838,2848,2854,2874,2884,2890,2900,2917,2922,2927,2932,2937,2946,2955,3000,3005,3010],{"__ignoreMap":188},[252,2556,2558,2560,2562,2565,2568,2571,2573,2575,2577],{"class":2557,"line":255},[254,1255],[252,2559,1681],{"class":282},[252,2561,263],{"class":262},[252,2563,2564],{"class":266}," resolve",[252,2566,2567],{"class":262}," }",[252,2569,2570],{"class":282}," from",[252,2572,1471],{"class":262},[252,2574,1436],{"class":900},[252,2576,1266],{"class":262},[252,2578,435],{"class":262},[252,2580,2581,2583,2585,2588,2590,2593,2595,2597,2599,2602,2604],{"class":254,"line":189},[252,2582,1681],{"class":282},[252,2584,263],{"class":262},[252,2586,2587],{"class":266}," fileURLToPath",[252,2589,270],{"class":262},[252,2591,2592],{"class":266}," URL",[252,2594,2567],{"class":262},[252,2596,2570],{"class":282},[252,2598,1471],{"class":262},[252,2600,2601],{"class":900},"node:url",[252,2603,1266],{"class":262},[252,2605,435],{"class":262},[252,2607,2608],{"class":254,"line":326},[252,2609,1695],{"emptyLinePlaceholder":198},[252,2611,2612,2614,2616,2619,2621,2623,2625,2628,2630],{"class":254,"line":343},[252,2613,1681],{"class":282},[252,2615,263],{"class":262},[252,2617,2618],{"class":266}," defineConfig",[252,2620,2567],{"class":262},[252,2622,2570],{"class":282},[252,2624,1471],{"class":262},[252,2626,2627],{"class":900},"vite",[252,2629,1266],{"class":262},[252,2631,435],{"class":262},[252,2633,2634,2636,2639,2641,2643,2646,2648],{"class":254,"line":354},[252,2635,1681],{"class":282},[252,2637,2638],{"class":266}," vue ",[252,2640,2134],{"class":282},[252,2642,1471],{"class":262},[252,2644,2645],{"class":900},"@vitejs\u002Fplugin-vue",[252,2647,1266],{"class":262},[252,2649,435],{"class":262},[252,2651,2652],{"class":254,"line":364},[252,2653,1695],{"emptyLinePlaceholder":198},[252,2655,2656],{"class":254,"line":381},[252,2657,2658],{"class":1223},"\u002F\u002F https:\u002F\u002Fvitejs.dev\u002Fconfig\u002F\n",[252,2660,2661,2663,2665,2667,2669],{"class":254,"line":398},[252,2662,1700],{"class":282},[252,2664,2154],{"class":282},[252,2666,2618],{"class":296},[252,2668,300],{"class":266},[252,2670,303],{"class":262},[252,2672,2673,2675,2677,2679,2681,2684],{"class":254,"line":415},[252,2674,1304],{"class":308},[252,2676,312],{"class":262},[252,2678,1263],{"class":266},[252,2680,1907],{"class":296},[252,2682,2683],{"class":266},"()]",[252,2685,323],{"class":262},[252,2687,2689,2692,2694],{"class":2688,"line":421},[254,1255],[252,2690,2691],{"class":308},"  build",[252,2693,312],{"class":262},[252,2695,351],{"class":262},[252,2697,2699,2702,2704],{"class":2698,"line":427},[254,1255],[252,2700,2701],{"class":308},"    lib",[252,2703,312],{"class":262},[252,2705,351],{"class":262},[252,2707,2709,2712,2714,2716,2719,2721,2723,2726,2728,2730],{"class":2708,"line":662},[254,1255],[252,2710,2711],{"class":308},"      entry",[252,2713,312],{"class":262},[252,2715,2564],{"class":296},[252,2717,2718],{"class":266},"(__dirname",[252,2720,270],{"class":262},[252,2722,1471],{"class":262},[252,2724,2725],{"class":900},"src\u002Fmain.ts",[252,2727,1266],{"class":262},[252,2729,432],{"class":266},[252,2731,323],{"class":262},[252,2733,2735,2737,2739,2741,2744,2746],{"class":2734,"line":668},[254,1255],[252,2736,1536],{"class":308},[252,2738,312],{"class":262},[252,2740,1471],{"class":262},[252,2742,2743],{"class":900},"Daisy",[252,2745,1266],{"class":262},[252,2747,323],{"class":262},[252,2749,2751,2754,2756,2758,2761,2763,2765,2768,2771,2774,2776,2778,2781,2784],{"class":2750,"line":674},[254,1255],[252,2752,2753],{"class":296},"      fileName",[252,2755,312],{"class":262},[252,2757,2279],{"class":262},[252,2759,2760],{"class":1990},"format",[252,2762,432],{"class":262},[252,2764,2287],{"class":258},[252,2766,2767],{"class":262}," `",[252,2769,2770],{"class":900},"daisy.",[252,2772,2773],{"class":262},"${",[252,2775,2760],{"class":266},[252,2777,276],{"class":262},[252,2779,2780],{"class":900},".js",[252,2782,2783],{"class":262},"`",[252,2785,323],{"class":262},[252,2787,2789,2792,2794,2796,2798,2801,2803,2805,2807,2810,2812,2814],{"class":2788,"line":1414},[254,1255],[252,2790,2791],{"class":308},"      formats",[252,2793,312],{"class":262},[252,2795,1263],{"class":266},[252,2797,1266],{"class":262},[252,2799,2800],{"class":900},"es",[252,2802,1266],{"class":262},[252,2804,270],{"class":262},[252,2806,1471],{"class":262},[252,2808,2809],{"class":900},"cjs",[252,2811,1266],{"class":262},[252,2813,1274],{"class":266},[252,2815,323],{"class":262},[252,2817,2819],{"class":2818,"line":1610},[254,1255],[252,2820,418],{"class":262},[252,2822,2824,2827,2829,2831,2834,2836],{"class":2823,"line":1618},[254,1255],[252,2825,2826],{"class":308},"    target",[252,2828,312],{"class":262},[252,2830,1471],{"class":262},[252,2832,2833],{"class":900},"esnext",[252,2835,1266],{"class":262},[252,2837,323],{"class":262},[252,2839,2841,2844,2846],{"class":2840,"line":1628},[254,1255],[252,2842,2843],{"class":308},"    rollupOptions",[252,2845,312],{"class":262},[252,2847,351],{"class":262},[252,2849,2851],{"class":2850,"line":1645},[254,1255],[252,2852,2853],{"class":1223},"      \u002F\u002F make sure to externalize deps that shouldn't be bundled into your library\n",[252,2855,2857,2860,2862,2864,2866,2868,2870,2872],{"class":2856,"line":1655},[254,1255],[252,2858,2859],{"class":308},"      external",[252,2861,312],{"class":262},[252,2863,1263],{"class":266},[252,2865,1266],{"class":262},[252,2867,1907],{"class":900},[252,2869,1266],{"class":262},[252,2871,1274],{"class":266},[252,2873,323],{"class":262},[252,2875,2877,2880,2882],{"class":2876,"line":1660},[254,1255],[252,2878,2879],{"class":308},"      output",[252,2881,312],{"class":262},[252,2883,351],{"class":262},[252,2885,2887],{"class":2886,"line":2363},[254,1255],[252,2888,2889],{"class":1223},"        \u002F\u002F Provide global variables to use in the UMD build for externalized deps\n",[252,2891,2893,2896,2898],{"class":2892,"line":2392},[254,1255],[252,2894,2895],{"class":308},"        globals",[252,2897,312],{"class":262},[252,2899,351],{"class":262},[252,2901,2903,2906,2908,2910,2913,2915],{"class":2902,"line":2407},[254,1255],[252,2904,2905],{"class":308},"          vue",[252,2907,312],{"class":262},[252,2909,1471],{"class":262},[252,2911,2912],{"class":900},"Vue",[252,2914,1266],{"class":262},[252,2916,323],{"class":262},[252,2918,2920],{"class":2919,"line":2421},[254,1255],[252,2921,1596],{"class":262},[252,2923,2925],{"class":2924,"line":2436},[254,1255],[252,2926,1602],{"class":262},[252,2928,2930],{"class":2929,"line":2441},[254,1255],[252,2931,418],{"class":262},[252,2933,2935],{"class":2934,"line":2446},[254,1255],[252,2936,424],{"class":262},[252,2938,2939,2942,2944],{"class":254,"line":2472},[252,2940,2941],{"class":308},"  resolve",[252,2943,312],{"class":262},[252,2945,351],{"class":262},[252,2947,2948,2951,2953],{"class":254,"line":2486},[252,2949,2950],{"class":308},"    alias",[252,2952,312],{"class":262},[252,2954,351],{"class":262},[252,2956,2957,2960,2962,2964,2966,2968,2970,2972,2974,2976,2978,2981,2983,2985,2988,2990,2993,2995,2998],{"class":254,"line":2501},[252,2958,2959],{"class":262},"      \"",[252,2961,1227],{"class":308},[252,2963,1266],{"class":262},[252,2965,312],{"class":262},[252,2967,2587],{"class":296},[252,2969,300],{"class":266},[252,2971,683],{"class":262},[252,2973,2592],{"class":296},[252,2975,300],{"class":266},[252,2977,1266],{"class":262},[252,2979,2980],{"class":900},".\u002Fsrc",[252,2982,1266],{"class":262},[252,2984,270],{"class":262},[252,2986,2987],{"class":282}," import",[252,2989,126],{"class":262},[252,2991,2992],{"class":266},"meta",[252,2994,126],{"class":262},[252,2996,2997],{"class":266},"url))",[252,2999,323],{"class":262},[252,3001,3003],{"class":254,"line":3002},32,[252,3004,418],{"class":262},[252,3006,3008],{"class":254,"line":3007},33,[252,3009,424],{"class":262},[252,3011,3013,3015,3017],{"class":254,"line":3012},34,[252,3014,276],{"class":262},[252,3016,432],{"class":266},[252,3018,435],{"class":262},[26,3020,3021,3022,3025,3026],{},"The next thing is to set the main entry file to the package (",[249,3023,3024],{},"main.ts"," in our case), so add those sections right after the ",[249,3027,3028],{},"\"script\"",[242,3030,3035],{"className":3031,"code":3032,"filename":3033,"language":3034,"meta":188,"style":188},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\"files\": [\n  \"dist\"\n],\n\"main\": \".\u002Fdist\u002Fdaisy.umd.cjs\",\n\"module\": \".\u002Fdist\u002Fdaisy.es.js\",\n\"types\": \".\u002Fdist\u002Fmain.d.ts\",\n\"exports\": {\n  \".\": {\n    \"import\": \".\u002Fdist\u002Fdaisy.es.js\",\n    \"require\": \".\u002Fdist\u002Fdaisy.umd.cjs\"\n  },\n  \".\u002Fdist\u002Fstyle.css\": \".\u002Fdist\u002Fstyle.css\"\n},\n","package.json","json",[249,3036,3037,3052,3063,3069,3088,3108,3128,3141,3153,3171,3188,3192,3209],{"__ignoreMap":188},[252,3038,3039,3041,3044,3046,3049],{"class":254,"line":255},[252,3040,1266],{"class":262},[252,3042,3043],{"class":900},"files",[252,3045,1266],{"class":262},[252,3047,3048],{"class":266},": ",[252,3050,3051],{"class":262},"[\n",[252,3053,3054,3057,3060],{"class":254,"line":189},[252,3055,3056],{"class":262},"  \"",[252,3058,3059],{"class":900},"dist",[252,3061,3062],{"class":262},"\"\n",[252,3064,3065,3067],{"class":254,"line":326},[252,3066,1274],{"class":262},[252,3068,323],{"class":266},[252,3070,3071,3073,3075,3077,3079,3081,3084,3086],{"class":254,"line":343},[252,3072,1266],{"class":262},[252,3074,879],{"class":900},[252,3076,1266],{"class":262},[252,3078,3048],{"class":266},[252,3080,1266],{"class":262},[252,3082,3083],{"class":900},".\u002Fdist\u002Fdaisy.umd.cjs",[252,3085,1266],{"class":262},[252,3087,323],{"class":266},[252,3089,3090,3092,3095,3097,3099,3101,3104,3106],{"class":254,"line":354},[252,3091,1266],{"class":262},[252,3093,3094],{"class":900},"module",[252,3096,1266],{"class":262},[252,3098,3048],{"class":266},[252,3100,1266],{"class":262},[252,3102,3103],{"class":900},".\u002Fdist\u002Fdaisy.es.js",[252,3105,1266],{"class":262},[252,3107,323],{"class":266},[252,3109,3110,3112,3115,3117,3119,3121,3124,3126],{"class":254,"line":364},[252,3111,1266],{"class":262},[252,3113,3114],{"class":900},"types",[252,3116,1266],{"class":262},[252,3118,3048],{"class":266},[252,3120,1266],{"class":262},[252,3122,3123],{"class":900},".\u002Fdist\u002Fmain.d.ts",[252,3125,1266],{"class":262},[252,3127,323],{"class":266},[252,3129,3130,3132,3135,3137,3139],{"class":254,"line":381},[252,3131,1266],{"class":262},[252,3133,3134],{"class":900},"exports",[252,3136,1266],{"class":262},[252,3138,3048],{"class":266},[252,3140,303],{"class":262},[252,3142,3143,3145,3147,3149,3151],{"class":254,"line":398},[252,3144,3056],{"class":262},[252,3146,126],{"class":258},[252,3148,1266],{"class":262},[252,3150,312],{"class":262},[252,3152,351],{"class":262},[252,3154,3155,3157,3159,3161,3163,3165,3167,3169],{"class":254,"line":415},[252,3156,1495],{"class":262},[252,3158,1681],{"class":896},[252,3160,1266],{"class":262},[252,3162,312],{"class":262},[252,3164,1471],{"class":262},[252,3166,3103],{"class":900},[252,3168,1266],{"class":262},[252,3170,323],{"class":262},[252,3172,3173,3175,3178,3180,3182,3184,3186],{"class":254,"line":421},[252,3174,1495],{"class":262},[252,3176,3177],{"class":896},"require",[252,3179,1266],{"class":262},[252,3181,312],{"class":262},[252,3183,1471],{"class":262},[252,3185,3083],{"class":900},[252,3187,3062],{"class":262},[252,3189,3190],{"class":254,"line":427},[252,3191,424],{"class":262},[252,3193,3194,3196,3199,3201,3203,3205,3207],{"class":254,"line":662},[252,3195,3056],{"class":262},[252,3197,3198],{"class":258},".\u002Fdist\u002Fstyle.css",[252,3200,1266],{"class":262},[252,3202,312],{"class":262},[252,3204,1471],{"class":262},[252,3206,3198],{"class":900},[252,3208,3062],{"class":262},[252,3210,3211,3213],{"class":254,"line":668},[252,3212,276],{"class":262},[252,3214,323],{"class":266},[26,3216,3217,3218,3220],{},"And last but not least is to export our ",[249,3219,1837],{}," component in our entry file",[242,3222,3224],{"className":2549,"code":3223,"filename":3024,"language":1930,"meta":188,"style":188},"export { default as DyFoo } from \".\u002Fcomponents\u002FDyFoo\u002FDyFoo.vue\";\n",[249,3225,3226],{"__ignoreMap":188},[252,3227,3228,3230,3232,3234,3237,3239,3241,3243,3245,3248,3250],{"class":254,"line":255},[252,3229,1700],{"class":282},[252,3231,263],{"class":262},[252,3233,2154],{"class":282},[252,3235,3236],{"class":282}," as",[252,3238,2182],{"class":266},[252,3240,2567],{"class":262},[252,3242,2570],{"class":282},[252,3244,1471],{"class":262},[252,3246,3247],{"class":900},".\u002Fcomponents\u002FDyFoo\u002FDyFoo.vue",[252,3249,1266],{"class":262},[252,3251,435],{"class":262},[26,3253,3254,3255,3258,3259,3261,3262,3264,3265,120,3268,3271],{},"You can now run a build (",[249,3256,3257],{},"npm run build",") and see that a ",[249,3260,3059],{}," directory is created, with it there are our ",[249,3263,1322],{}," file and 2 new files ",[249,3266,3267],{},"daisy.cjs.js",[249,3269,3270],{},"daisy.es.js",". Basically that's it, we're all good. But if you want to add typing for Typescript support you'll want to add couple more things.",[26,3273,3274,3275,3278,3279,3282],{},"First, edit the ",[249,3276,3277],{},"build-only"," npm script to emit types declarations with ",[249,3280,3281],{},"vue-tsc --emitDeclarationOnly",", like this",[242,3284,3286],{"className":3031,"code":3285,"filename":3033,"language":3034,"meta":188,"style":188},"\"build-only\": \"vite build && vue-tsc --emitDeclarationOnly\",\n",[249,3287,3288],{"__ignoreMap":188},[252,3289,3290,3292,3294,3296,3298,3300,3303,3305],{"class":254,"line":255},[252,3291,1266],{"class":262},[252,3293,3277],{"class":900},[252,3295,1266],{"class":262},[252,3297,3048],{"class":266},[252,3299,1266],{"class":262},[252,3301,3302],{"class":900},"vite build && vue-tsc --emitDeclarationOnly",[252,3304,1266],{"class":262},[252,3306,323],{"class":266},[26,3308,3309,3310,3313,3314,3317,3318,3321],{},"and then add ",[249,3311,3312],{},"compilerOptions"," and the files to ",[249,3315,3316],{},"include"," (right after the ",[249,3319,3320],{},"references"," array)",[242,3323,3326],{"className":3031,"code":3324,"filename":3325,"language":3034,"meta":188,"style":188},"\"compilerOptions\": {\n  \"lib\": [\"ESNext\", \"DOM\"],\n  \"skipLibCheck\": true,\n  \"types\": [\"vite\u002Fclient\"],\n  \"outDir\": \"dist\",\n  \"declaration\": true\n},\n\"include\": [\"src\u002F**\u002F*.vue\", \"src\u002Fmain.ts\"]\n","tsconfig.json",[249,3327,3328,3340,3372,3386,3407,3426,3440,3446],{"__ignoreMap":188},[252,3329,3330,3332,3334,3336,3338],{"class":254,"line":255},[252,3331,1266],{"class":262},[252,3333,3312],{"class":900},[252,3335,1266],{"class":262},[252,3337,3048],{"class":266},[252,3339,303],{"class":262},[252,3341,3342,3344,3347,3349,3351,3353,3355,3358,3360,3362,3364,3367,3369],{"class":254,"line":189},[252,3343,3056],{"class":262},[252,3345,3346],{"class":258},"lib",[252,3348,1266],{"class":262},[252,3350,312],{"class":262},[252,3352,1263],{"class":262},[252,3354,1266],{"class":262},[252,3356,3357],{"class":900},"ESNext",[252,3359,1266],{"class":262},[252,3361,270],{"class":262},[252,3363,1471],{"class":262},[252,3365,3366],{"class":900},"DOM",[252,3368,1266],{"class":262},[252,3370,3371],{"class":262},"],\n",[252,3373,3374,3376,3379,3381,3383],{"class":254,"line":326},[252,3375,3056],{"class":262},[252,3377,3378],{"class":258},"skipLibCheck",[252,3380,1266],{"class":262},[252,3382,312],{"class":262},[252,3384,3385],{"class":262}," true,\n",[252,3387,3388,3390,3392,3394,3396,3398,3400,3403,3405],{"class":254,"line":343},[252,3389,3056],{"class":262},[252,3391,3114],{"class":258},[252,3393,1266],{"class":262},[252,3395,312],{"class":262},[252,3397,1263],{"class":262},[252,3399,1266],{"class":262},[252,3401,3402],{"class":900},"vite\u002Fclient",[252,3404,1266],{"class":262},[252,3406,3371],{"class":262},[252,3408,3409,3411,3414,3416,3418,3420,3422,3424],{"class":254,"line":354},[252,3410,3056],{"class":262},[252,3412,3413],{"class":258},"outDir",[252,3415,1266],{"class":262},[252,3417,312],{"class":262},[252,3419,1471],{"class":262},[252,3421,3059],{"class":900},[252,3423,1266],{"class":262},[252,3425,323],{"class":262},[252,3427,3428,3430,3433,3435,3437],{"class":254,"line":364},[252,3429,3056],{"class":262},[252,3431,3432],{"class":258},"declaration",[252,3434,1266],{"class":262},[252,3436,312],{"class":262},[252,3438,3439],{"class":262}," true\n",[252,3441,3442,3444],{"class":254,"line":381},[252,3443,276],{"class":262},[252,3445,323],{"class":266},[252,3447,3448,3450,3452,3454,3456,3459,3461,3464,3466,3468,3470,3472,3474],{"class":254,"line":398},[252,3449,1266],{"class":262},[252,3451,3316],{"class":900},[252,3453,1266],{"class":262},[252,3455,3048],{"class":266},[252,3457,3458],{"class":262},"[",[252,3460,1266],{"class":262},[252,3462,3463],{"class":900},"src\u002F**\u002F*.vue",[252,3465,1266],{"class":262},[252,3467,270],{"class":262},[252,3469,1471],{"class":262},[252,3471,2725],{"class":900},[252,3473,1266],{"class":262},[252,3475,3476],{"class":262},"]\n",[458,3478,3480],{"id":3479},"_4-publish-your-vite-package-into-github-packages-with-github-actions","4. Publish your vite package into GitHub packages with GitHub actions",[26,3482,3483,3484,3486,3487,983],{},"Now that you can build your vite library locally on your machine, it's time to build it in the cloud, in GitHub actions, automatically with every commit to ",[249,3485,879],{}," branch. We'll also setup a step in our workflow to bump the release version (with ",[58,3488,3491],{"href":3489,"rel":3490},"https:\u002F\u002Fgithub.com\u002Fsemantic-release\u002Fsemantic-release",[62],"semantic-release",[26,3493,3494],{},"So let's create a GitHub action workflow",[242,3496,3498],{"className":886,"code":3497,"filename":888,"language":889,"meta":188,"style":188},"touch .github\u002Fworkflows\u002Fcd.yaml\n",[249,3499,3500],{"__ignoreMap":188},[252,3501,3502,3504],{"class":254,"line":255},[252,3503,1869],{"class":896},[252,3505,3506],{"class":900}," .github\u002Fworkflows\u002Fcd.yaml\n",[26,3508,3509],{},"And put there 3 steps:",[866,3511,3512,3515,3521],{},[154,3513,3514],{},"Test our components (currently just linting).",[154,3516,3517,3518,983],{},"Publish a release to GitHub (using ",[58,3519,3491],{"href":3489,"rel":3520},[62],[154,3522,3523],{},"Publish the package to GitHub registry.",[242,3525,3530],{"className":3526,"code":3527,"filename":3528,"language":3529,"meta":188,"style":188},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","name: Continuous Deployment\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    name: Test the project\n\n    steps:\n      - uses: actions\u002Fcheckout@v3\n\n      - name: Use Node.js\n        uses: actions\u002Fsetup-node@v2\n        with:\n          node-version: \"16\"\n\n      - name: Install Dependencies\n        run: npm install\n\n      - name: Run Linter\n        run: npm run lint\n\n  publish-release:\n    needs: test\n    runs-on: ubuntu-latest\n    name: Publish a release to GitHub\n    outputs:\n      version: ${{ steps.release_version.outputs.version }}\n    steps:\n      - uses: actions\u002Fcheckout@v3\n\n      - name: Create Release 🚀\n        uses: ridedott\u002Frelease-me-action@master\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          release-branches: '[\"main\"]'\n          node-module: true\n\n  publish-gpr:\n    needs: publish-release\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      packages: write\n    steps:\n      - uses: actions\u002Fcheckout@v3\n\n      - name: Install Dependencies\n        run: npm install\n\n      - name: Build the project\n        run: npm run build\n\n      - uses: actions\u002Fsetup-node@v3\n        with:\n          node-version: 16\n          registry-url: https:\u002F\u002Fnpm.pkg.github.com\u002F\n          scope: \"@nirgn975\"\n      - run: npm ci\n      - run: npm publish\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n",".github\u002Fworkflows\u002Fcd.yaml","yaml",[249,3531,3532,3542,3546,3554,3561,3568,3576,3580,3587,3594,3604,3613,3617,3624,3636,3640,3652,3662,3669,3683,3687,3698,3708,3712,3723,3732,3736,3743,3753,3761,3770,3777,3787,3793,3803,3808,3820,3830,3838,3849,3856,3872,3882,3887,3895,3905,3914,3922,3933,3944,3951,3962,3967,3978,3987,3992,4004,4014,4019,4031,4038,4048,4059,4074,4086,4098,4105],{"__ignoreMap":188},[252,3533,3534,3537,3539],{"class":254,"line":255},[252,3535,3536],{"class":308},"name",[252,3538,312],{"class":262},[252,3540,3541],{"class":900}," Continuous Deployment\n",[252,3543,3544],{"class":254,"line":189},[252,3545,1695],{"emptyLinePlaceholder":198},[252,3547,3548,3551],{"class":254,"line":326},[252,3549,3550],{"class":2415},"on",[252,3552,3553],{"class":262},":\n",[252,3555,3556,3559],{"class":254,"line":343},[252,3557,3558],{"class":308},"  push",[252,3560,3553],{"class":262},[252,3562,3563,3566],{"class":254,"line":354},[252,3564,3565],{"class":308},"    branches",[252,3567,3553],{"class":262},[252,3569,3570,3573],{"class":254,"line":364},[252,3571,3572],{"class":262},"      -",[252,3574,3575],{"class":900}," main\n",[252,3577,3578],{"class":254,"line":381},[252,3579,1695],{"emptyLinePlaceholder":198},[252,3581,3582,3585],{"class":254,"line":398},[252,3583,3584],{"class":308},"jobs",[252,3586,3553],{"class":262},[252,3588,3589,3592],{"class":254,"line":415},[252,3590,3591],{"class":308},"  test",[252,3593,3553],{"class":262},[252,3595,3596,3599,3601],{"class":254,"line":421},[252,3597,3598],{"class":308},"    runs-on",[252,3600,312],{"class":262},[252,3602,3603],{"class":900}," ubuntu-latest\n",[252,3605,3606,3608,3610],{"class":254,"line":427},[252,3607,1631],{"class":308},[252,3609,312],{"class":262},[252,3611,3612],{"class":900}," Test the project\n",[252,3614,3615],{"class":254,"line":662},[252,3616,1695],{"emptyLinePlaceholder":198},[252,3618,3619,3622],{"class":254,"line":668},[252,3620,3621],{"class":308},"    steps",[252,3623,3553],{"class":262},[252,3625,3626,3628,3631,3633],{"class":254,"line":674},[252,3627,3572],{"class":262},[252,3629,3630],{"class":308}," uses",[252,3632,312],{"class":262},[252,3634,3635],{"class":900}," actions\u002Fcheckout@v3\n",[252,3637,3638],{"class":254,"line":1414},[252,3639,1695],{"emptyLinePlaceholder":198},[252,3641,3642,3644,3647,3649],{"class":254,"line":1610},[252,3643,3572],{"class":262},[252,3645,3646],{"class":308}," name",[252,3648,312],{"class":262},[252,3650,3651],{"class":900}," Use Node.js\n",[252,3653,3654,3657,3659],{"class":254,"line":1618},[252,3655,3656],{"class":308},"        uses",[252,3658,312],{"class":262},[252,3660,3661],{"class":900}," actions\u002Fsetup-node@v2\n",[252,3663,3664,3667],{"class":254,"line":1628},[252,3665,3666],{"class":308},"        with",[252,3668,3553],{"class":262},[252,3670,3671,3674,3676,3678,3681],{"class":254,"line":1645},[252,3672,3673],{"class":308},"          node-version",[252,3675,312],{"class":262},[252,3677,1471],{"class":262},[252,3679,3680],{"class":900},"16",[252,3682,3062],{"class":262},[252,3684,3685],{"class":254,"line":1655},[252,3686,1695],{"emptyLinePlaceholder":198},[252,3688,3689,3691,3693,3695],{"class":254,"line":1660},[252,3690,3572],{"class":262},[252,3692,3646],{"class":308},[252,3694,312],{"class":262},[252,3696,3697],{"class":900}," Install Dependencies\n",[252,3699,3700,3703,3705],{"class":254,"line":2363},[252,3701,3702],{"class":308},"        run",[252,3704,312],{"class":262},[252,3706,3707],{"class":900}," npm install\n",[252,3709,3710],{"class":254,"line":2392},[252,3711,1695],{"emptyLinePlaceholder":198},[252,3713,3714,3716,3718,3720],{"class":254,"line":2407},[252,3715,3572],{"class":262},[252,3717,3646],{"class":308},[252,3719,312],{"class":262},[252,3721,3722],{"class":900}," Run Linter\n",[252,3724,3725,3727,3729],{"class":254,"line":2421},[252,3726,3702],{"class":308},[252,3728,312],{"class":262},[252,3730,3731],{"class":900}," npm run lint\n",[252,3733,3734],{"class":254,"line":2436},[252,3735,1695],{"emptyLinePlaceholder":198},[252,3737,3738,3741],{"class":254,"line":2441},[252,3739,3740],{"class":308},"  publish-release",[252,3742,3553],{"class":262},[252,3744,3745,3748,3750],{"class":254,"line":2446},[252,3746,3747],{"class":308},"    needs",[252,3749,312],{"class":262},[252,3751,3752],{"class":900}," test\n",[252,3754,3755,3757,3759],{"class":254,"line":2472},[252,3756,3598],{"class":308},[252,3758,312],{"class":262},[252,3760,3603],{"class":900},[252,3762,3763,3765,3767],{"class":254,"line":2486},[252,3764,1631],{"class":308},[252,3766,312],{"class":262},[252,3768,3769],{"class":900}," Publish a release to GitHub\n",[252,3771,3772,3775],{"class":254,"line":2501},[252,3773,3774],{"class":308},"    outputs",[252,3776,3553],{"class":262},[252,3778,3779,3782,3784],{"class":254,"line":3002},[252,3780,3781],{"class":308},"      version",[252,3783,312],{"class":262},[252,3785,3786],{"class":900}," ${{ steps.release_version.outputs.version }}\n",[252,3788,3789,3791],{"class":254,"line":3007},[252,3790,3621],{"class":308},[252,3792,3553],{"class":262},[252,3794,3795,3797,3799,3801],{"class":254,"line":3012},[252,3796,3572],{"class":262},[252,3798,3630],{"class":308},[252,3800,312],{"class":262},[252,3802,3635],{"class":900},[252,3804,3806],{"class":254,"line":3805},35,[252,3807,1695],{"emptyLinePlaceholder":198},[252,3809,3811,3813,3815,3817],{"class":254,"line":3810},36,[252,3812,3572],{"class":262},[252,3814,3646],{"class":308},[252,3816,312],{"class":262},[252,3818,3819],{"class":900}," Create Release 🚀\n",[252,3821,3823,3825,3827],{"class":254,"line":3822},37,[252,3824,3656],{"class":308},[252,3826,312],{"class":262},[252,3828,3829],{"class":900}," ridedott\u002Frelease-me-action@master\n",[252,3831,3833,3836],{"class":254,"line":3832},38,[252,3834,3835],{"class":308},"        env",[252,3837,3553],{"class":262},[252,3839,3841,3844,3846],{"class":254,"line":3840},39,[252,3842,3843],{"class":308},"          GITHUB_TOKEN",[252,3845,312],{"class":262},[252,3847,3848],{"class":900}," ${{ secrets.GITHUB_TOKEN }}\n",[252,3850,3852,3854],{"class":254,"line":3851},40,[252,3853,3666],{"class":308},[252,3855,3553],{"class":262},[252,3857,3859,3862,3864,3866,3869],{"class":254,"line":3858},41,[252,3860,3861],{"class":308},"          release-branches",[252,3863,312],{"class":262},[252,3865,910],{"class":262},[252,3867,3868],{"class":900},"[\"main\"]",[252,3870,3871],{"class":262},"'\n",[252,3873,3875,3878,3880],{"class":254,"line":3874},42,[252,3876,3877],{"class":308},"          node-module",[252,3879,312],{"class":262},[252,3881,3439],{"class":2415},[252,3883,3885],{"class":254,"line":3884},43,[252,3886,1695],{"emptyLinePlaceholder":198},[252,3888,3890,3893],{"class":254,"line":3889},44,[252,3891,3892],{"class":308},"  publish-gpr",[252,3894,3553],{"class":262},[252,3896,3898,3900,3902],{"class":254,"line":3897},45,[252,3899,3747],{"class":308},[252,3901,312],{"class":262},[252,3903,3904],{"class":900}," publish-release\n",[252,3906,3908,3910,3912],{"class":254,"line":3907},46,[252,3909,3598],{"class":308},[252,3911,312],{"class":262},[252,3913,3603],{"class":900},[252,3915,3917,3920],{"class":254,"line":3916},47,[252,3918,3919],{"class":308},"    permissions",[252,3921,3553],{"class":262},[252,3923,3925,3928,3930],{"class":254,"line":3924},48,[252,3926,3927],{"class":308},"      contents",[252,3929,312],{"class":262},[252,3931,3932],{"class":900}," read\n",[252,3934,3936,3939,3941],{"class":254,"line":3935},49,[252,3937,3938],{"class":308},"      packages",[252,3940,312],{"class":262},[252,3942,3943],{"class":900}," write\n",[252,3945,3947,3949],{"class":254,"line":3946},50,[252,3948,3621],{"class":308},[252,3950,3553],{"class":262},[252,3952,3954,3956,3958,3960],{"class":254,"line":3953},51,[252,3955,3572],{"class":262},[252,3957,3630],{"class":308},[252,3959,312],{"class":262},[252,3961,3635],{"class":900},[252,3963,3965],{"class":254,"line":3964},52,[252,3966,1695],{"emptyLinePlaceholder":198},[252,3968,3970,3972,3974,3976],{"class":254,"line":3969},53,[252,3971,3572],{"class":262},[252,3973,3646],{"class":308},[252,3975,312],{"class":262},[252,3977,3697],{"class":900},[252,3979,3981,3983,3985],{"class":254,"line":3980},54,[252,3982,3702],{"class":308},[252,3984,312],{"class":262},[252,3986,3707],{"class":900},[252,3988,3990],{"class":254,"line":3989},55,[252,3991,1695],{"emptyLinePlaceholder":198},[252,3993,3995,3997,3999,4001],{"class":254,"line":3994},56,[252,3996,3572],{"class":262},[252,3998,3646],{"class":308},[252,4000,312],{"class":262},[252,4002,4003],{"class":900}," Build the project\n",[252,4005,4007,4009,4011],{"class":254,"line":4006},57,[252,4008,3702],{"class":308},[252,4010,312],{"class":262},[252,4012,4013],{"class":900}," npm run build\n",[252,4015,4017],{"class":254,"line":4016},58,[252,4018,1695],{"emptyLinePlaceholder":198},[252,4020,4022,4024,4026,4028],{"class":254,"line":4021},59,[252,4023,3572],{"class":262},[252,4025,3630],{"class":308},[252,4027,312],{"class":262},[252,4029,4030],{"class":900}," actions\u002Fsetup-node@v3\n",[252,4032,4034,4036],{"class":254,"line":4033},60,[252,4035,3666],{"class":308},[252,4037,3553],{"class":262},[252,4039,4041,4043,4045],{"class":254,"line":4040},61,[252,4042,3673],{"class":308},[252,4044,312],{"class":262},[252,4046,4047],{"class":1782}," 16\n",[252,4049,4051,4054,4056],{"class":254,"line":4050},62,[252,4052,4053],{"class":308},"          registry-url",[252,4055,312],{"class":262},[252,4057,4058],{"class":900}," https:\u002F\u002Fnpm.pkg.github.com\u002F\n",[252,4060,4062,4065,4067,4069,4072],{"class":254,"line":4061},63,[252,4063,4064],{"class":308},"          scope",[252,4066,312],{"class":262},[252,4068,1471],{"class":262},[252,4070,4071],{"class":900},"@nirgn975",[252,4073,3062],{"class":262},[252,4075,4077,4079,4081,4083],{"class":254,"line":4076},64,[252,4078,3572],{"class":262},[252,4080,1147],{"class":308},[252,4082,312],{"class":262},[252,4084,4085],{"class":900}," npm ci\n",[252,4087,4089,4091,4093,4095],{"class":254,"line":4088},65,[252,4090,3572],{"class":262},[252,4092,1147],{"class":308},[252,4094,312],{"class":262},[252,4096,4097],{"class":900}," npm publish\n",[252,4099,4101,4103],{"class":254,"line":4100},66,[252,4102,3835],{"class":308},[252,4104,3553],{"class":262},[252,4106,4108,4111,4113],{"class":254,"line":4107},67,[252,4109,4110],{"class":308},"          NODE_AUTH_TOKEN",[252,4112,312],{"class":262},[252,4114,3848],{"class":900},[26,4116,4117,4118,4120,4121,4124,4125,4128],{},"We also need to change a few things in our ",[249,4119,3033],{},". First the ",[249,4122,4123],{},"\"name\""," should start with your organization name (or username). Second, you should add a ",[249,4126,4127],{},"repository"," section",[242,4130,4132],{"className":3031,"code":4131,"filename":3033,"language":3034,"meta":188,"style":188},"\"repository\": {\n  \"type\": \"git\",\n  \"url\": \"git:\u002F\u002Fgithub.com\u002Fnirgn975\u002Fdaisy.git\"\n},\n",[249,4133,4134,4146,4164,4182],{"__ignoreMap":188},[252,4135,4136,4138,4140,4142,4144],{"class":254,"line":255},[252,4137,1266],{"class":262},[252,4139,4127],{"class":900},[252,4141,1266],{"class":262},[252,4143,3048],{"class":266},[252,4145,303],{"class":262},[252,4147,4148,4150,4152,4154,4156,4158,4160,4162],{"class":254,"line":189},[252,4149,3056],{"class":262},[252,4151,1231],{"class":258},[252,4153,1266],{"class":262},[252,4155,312],{"class":262},[252,4157,1471],{"class":262},[252,4159,897],{"class":900},[252,4161,1266],{"class":262},[252,4163,323],{"class":262},[252,4165,4166,4168,4171,4173,4175,4177,4180],{"class":254,"line":326},[252,4167,3056],{"class":262},[252,4169,4170],{"class":258},"url",[252,4172,1266],{"class":262},[252,4174,312],{"class":262},[252,4176,1471],{"class":262},[252,4178,4179],{"class":900},"git:\u002F\u002Fgithub.com\u002Fnirgn975\u002Fdaisy.git",[252,4181,3062],{"class":262},[252,4183,4184,4186],{"class":254,"line":343},[252,4185,276],{"class":262},[252,4187,323],{"class":266},[26,4189,4190],{},"Once you commit all the changes and push your branch to GitHub, a new GitHub workflow will automatically start, and once it finishes you'll see the package in your \"packages\" section inside the repository.",[26,4192,4193],{},[41,4194],{"alt":4195,"className":4196,"src":4197},"GitHub Package",[45,46],"\u002Fposts\u002F2022\u002Fdesign-system\u002Fgithub-package.webp",[26,4199,4200,4201,1894,4203,4206,4207,126],{},"If you want to publish to ",[249,4202,1077],{},[249,4204,4205],{},"yarn"," instead, you can follow the ",[58,4208,4211],{"href":4209,"rel":4210},"https:\u002F\u002Fdocs.github.com\u002Fen\u002Factions\u002Fpublishing-packages\u002Fpublishing-nodejs-packages",[62],"github publishing-nodejs-packages guide",[458,4213,4215],{"id":4214},"_5-publish-your-storybook-to-chromatic","5. Publish your Storybook to Chromatic",[26,4217,4218,4219,4224],{},"First thing we need to do is ask ourselves what is ",[58,4220,4223],{"href":4221,"rel":4222},"https:\u002F\u002Fwww.chromatic.com",[62],"Chromatic","? and why we should want to publish our Storybook to that platform?",[26,4226,4227],{},"Chromatic is a cloud solution for Storybook that offers: Free Storybook hosting, allowing you to publish your Storybooks with local or remote changes, visual regression testing with cross-browser support, DOM snapshots to pick code changes, gathering UI feedback on PRs, and more.",[26,4229,4230,4231,4236],{},"Now that we understand what is Chromatic and have a good reason to want to use it, let's ",[58,4232,4235],{"href":4233,"rel":4234},"https:\u002F\u002Fwww.chromatic.com\u002Fstart?startWithSignup=true",[62],"sign up on their website"," (I just sign-in with my GitHub account) and add a new step in our GitHub action to push our storybook and host it on their platform.",[26,4238,4239,4240,4243,4244,4247,4248,4251,4252,4255],{},"If it's your first time on ",[58,4241,4223],{"href":4221,"rel":4242},[62],", go to ",[871,4245,4246],{},"\"projects\""," and click on ",[871,4249,4250],{},"\"Choose from GitHub\""," and a list of all your GitHub repositories will be opened, choose the repo you have used for this Design System and you'll get a ",[249,4253,4254],{},"token"," for that project, like in the screenshot below.",[26,4257,4258],{},[41,4259],{"alt":4260,"className":4261,"src":4262},"Chromatic API Key",[45,46],"\u002Fposts\u002F2022\u002Fdesign-system\u002Fchromatic-key.webp",[26,4264,4265,4266,4268,4269,4274],{},"We can use that ",[249,4267,4254],{}," to push our Storybook directly to Chromatic (with Chromatic npm package), but it's more convenient to just use ",[58,4270,4273],{"href":4271,"rel":4272},"https:\u002F\u002Fgithub.com\u002Fchromaui\u002Faction",[62],"their GitHub action step"," and add it to our workflow at the end of the file",[242,4276,4278],{"className":3526,"code":4277,"filename":3528,"language":3529,"meta":188,"style":188},"deploy_chromatic:\n  needs: [publish-release]\n  name: Deploy Design System to Chromatic\n  runs-on: ubuntu-latest\n\n  steps:\n    - name: Checkout repository\n      uses: actions\u002Fcheckout@v3\n      with:\n        fetch-depth: 0\n\n    - name: Install dependencies\n      run: npm install\n\n    - name: Publish to Chromatic\n      uses: chromaui\u002Faction@v1\n      with:\n        token: ${{ secrets.GITHUB_TOKEN }}\n        projectToken: ${{ secrets.CHROMATIC_TOKEN }}\n",[249,4279,4280,4287,4301,4311,4320,4324,4331,4343,4352,4359,4369,4373,4384,4393,4397,4408,4417,4423,4432],{"__ignoreMap":188},[252,4281,4282,4285],{"class":254,"line":255},[252,4283,4284],{"class":308},"deploy_chromatic",[252,4286,3553],{"class":262},[252,4288,4289,4292,4294,4296,4299],{"class":254,"line":189},[252,4290,4291],{"class":308},"  needs",[252,4293,312],{"class":262},[252,4295,1263],{"class":262},[252,4297,4298],{"class":900},"publish-release",[252,4300,3476],{"class":262},[252,4302,4303,4306,4308],{"class":254,"line":326},[252,4304,4305],{"class":308},"  name",[252,4307,312],{"class":262},[252,4309,4310],{"class":900}," Deploy Design System to Chromatic\n",[252,4312,4313,4316,4318],{"class":254,"line":343},[252,4314,4315],{"class":308},"  runs-on",[252,4317,312],{"class":262},[252,4319,3603],{"class":900},[252,4321,4322],{"class":254,"line":354},[252,4323,1695],{"emptyLinePlaceholder":198},[252,4325,4326,4329],{"class":254,"line":364},[252,4327,4328],{"class":308},"  steps",[252,4330,3553],{"class":262},[252,4332,4333,4336,4338,4340],{"class":254,"line":381},[252,4334,4335],{"class":262},"    -",[252,4337,3646],{"class":308},[252,4339,312],{"class":262},[252,4341,4342],{"class":900}," Checkout repository\n",[252,4344,4345,4348,4350],{"class":254,"line":398},[252,4346,4347],{"class":308},"      uses",[252,4349,312],{"class":262},[252,4351,3635],{"class":900},[252,4353,4354,4357],{"class":254,"line":415},[252,4355,4356],{"class":308},"      with",[252,4358,3553],{"class":262},[252,4360,4361,4364,4366],{"class":254,"line":421},[252,4362,4363],{"class":308},"        fetch-depth",[252,4365,312],{"class":262},[252,4367,4368],{"class":1782}," 0\n",[252,4370,4371],{"class":254,"line":427},[252,4372,1695],{"emptyLinePlaceholder":198},[252,4374,4375,4377,4379,4381],{"class":254,"line":662},[252,4376,4335],{"class":262},[252,4378,3646],{"class":308},[252,4380,312],{"class":262},[252,4382,4383],{"class":900}," Install dependencies\n",[252,4385,4386,4389,4391],{"class":254,"line":668},[252,4387,4388],{"class":308},"      run",[252,4390,312],{"class":262},[252,4392,3707],{"class":900},[252,4394,4395],{"class":254,"line":674},[252,4396,1695],{"emptyLinePlaceholder":198},[252,4398,4399,4401,4403,4405],{"class":254,"line":1414},[252,4400,4335],{"class":262},[252,4402,3646],{"class":308},[252,4404,312],{"class":262},[252,4406,4407],{"class":900}," Publish to Chromatic\n",[252,4409,4410,4412,4414],{"class":254,"line":1610},[252,4411,4347],{"class":308},[252,4413,312],{"class":262},[252,4415,4416],{"class":900}," chromaui\u002Faction@v1\n",[252,4418,4419,4421],{"class":254,"line":1618},[252,4420,4356],{"class":308},[252,4422,3553],{"class":262},[252,4424,4425,4428,4430],{"class":254,"line":1628},[252,4426,4427],{"class":308},"        token",[252,4429,312],{"class":262},[252,4431,3848],{"class":900},[252,4433,4434,4437,4439],{"class":254,"line":1645},[252,4435,4436],{"class":308},"        projectToken",[252,4438,312],{"class":262},[252,4440,4441],{"class":900}," ${{ secrets.CHROMATIC_TOKEN }}\n",[26,4443,4444,4445,4448,4449,4452,4453,4456,4457,126],{},"The token you got from Chromatic should stay a secret that only you know (as well as any other API or access tokens), or else everyone with that token will have the power to push to your Chromatic project and change things, that's why we use ",[249,4446,4447],{},"${{ secrets.CHROMATIC_TOKEN }}"," as our Chromatic ",[249,4450,4451],{},"projectToken",", that means we can hide the actual token from the source code, and we'll control it via the ",[871,4454,4455],{},"\"Secret\""," section in the repository ",[871,4458,4459],{},"\"Settings\"",[26,4461,4462,4463,4466,4467,4470],{},"So all you need to do is to create a new secret, give it a name (",[249,4464,4465],{},"CHROMATIC_TOKEN",") and insert your token as its value (",[249,4468,4469],{},"a85f7b330490"," in our case)",[26,4472,4473],{},[41,4474],{"alt":4475,"className":4476,"src":4477},"GitHub Secrets",[45,46],"\u002Fposts\u002F2022\u002Fdesign-system\u002Fgithub-secrets.webp",[26,4479,4480,4481,4484],{},"That's it, we're ready to commit and push our branch to GitHub and our workflow will push our Storybook to ",[58,4482,4223],{"href":4221,"rel":4483},[62],". Once your GitHub workflow finished running you can see your Chromatic project, in the next commit Chromatic will run and catch visual changes and error, and you also have a free hosting for your Storybook.",[26,4486,4487],{},[41,4488],{"alt":4489,"className":4490,"src":4491},"Storybook hosting on Chromatic",[45,46],"\u002Fposts\u002F2022\u002Fdesign-system\u002Fchromatic-storybook.webp",[458,4493,4495],{"id":4494},"_6-summary","6. Summary",[26,4497,4498],{},"Wow! We have done a lot and we're ready to create some new components for our component library, and everything will just work automagically (bump version, release and publish package, etc).",[26,4500,4501],{},"We can now use our Vue.js component library in other frontend projects at our startup, develop new components in isolation with Storybook, and test for visual regression with Chromatic.",[772,4503,4504],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s6hCs, html code.shiki .s6hCs{--shiki-light:#9C3EDA;--shiki-light-font-style:italic;--shiki-default:#C792EA;--shiki-default-font-style:italic;--shiki-dark:#C792EA;--shiki-dark-font-style:italic}html pre.shiki code .sFweD, html code.shiki .sFweD{--shiki-light:#E2931D;--shiki-light-font-style:italic;--shiki-default:#FFCB6B;--shiki-default-font-style:italic;--shiki-dark:#FFCB6B;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":188,"searchDepth":189,"depth":189,"links":4506},[4507,4508,4509,4510,4511,4512],{"id":1059,"depth":189,"text":1060},{"id":1819,"depth":189,"text":1820},{"id":2519,"depth":189,"text":2520},{"id":3479,"depth":189,"text":3480},{"id":4214,"depth":189,"text":4215},{"id":4494,"depth":189,"text":4495},"2022-09-01","A practical guide to building a Vue.js component library with Storybook.",{"src":4516},"\u002Fposts\u002F2022\u002Fdesign-system\u002Fhero.webp",{},"\u002Fblog\u002F2022\u002Fdesign-system",{"title":1005,"description":4514},"blog\u002F2022\u002Fdesign-system","-NFwndVBvKNJiM54oTB3er6GG0OxXxga8jI33F6dl44",{"id":4523,"title":4524,"author":4525,"badge":4527,"body":4529,"date":4862,"description":4863,"draft":193,"extension":194,"image":4864,"meta":4866,"navigation":198,"path":4867,"seo":4868,"stem":4869,"__hash__":4870},"posts\u002Fblog\u002F2021\u002Fhugo-theme.md","Hugo Theme With Developer Focus In Mind",{"name":16,"to":17,"avatar":4526},{"src":19},{"label":4528},"open source contribution",{"type":23,"value":4530,"toc":4854},[4531,4540,4555,4558,4562,4576,4586,4593,4602,4605,4672,4676,4683,4728,4731,4743,4759,4764,4770,4775,4798,4802,4805,4815,4836,4840,4843,4851],[26,4532,4533,4534,4539],{},"Lately I was redesigning ",[58,4535,4538],{"href":4536,"rel":4537},"http:\u002F\u002Fnir.galon.io",[62],"my own resume website",". I consider it version 4 of the website, because it's the fourth major change I did to it. This website has been running for quite a few years now, and every now and then I want to change the underlying technology that I use or redesign it, so I spend a weekend and do it.",[26,4541,4542,4543,4548,4549,4554],{},"This time I also wanted to change the design, but not that drastically. The main thing I wanted to change is the underlying template generator (from ",[58,4544,4547],{"href":4545,"rel":4546},"https:\u002F\u002Fjekyllrb.com",[62],"Jekyll"," to ",[58,4550,4553],{"href":4551,"rel":4552},"https:\u002F\u002Fgohugo.io",[62],"Hugo","), because I already use Hugo in my blog and I just love it. But I also wanted to add 2 sections that I never saw in any template (and I searched a lot because at first I didn't want to put the time and develop it myself): first is GitHub repos (projects) I want to highlight in my resume. The second section is for the blog, automatically get the latest posts in there.",[26,4556,4557],{},"So, I spent the weekend and did it. I was very pleased with the results. So pleased that I thought other people will also want this design and features in their own resume website.",[458,4559,4561],{"id":4560},"_1-hugo-theme","1. Hugo Theme",[26,4563,4564,4565,4570,4571,126],{},"It took me some time to really sit down and do it, but I finally did it. I converted my own resume website to a Hugo theme and I'm ",[58,4566,4569],{"href":4567,"rel":4568},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FEating_your_own_dog_food",[62],"eating my own dog food"," - my own resume website uses it, you can check the ",[58,4572,4575],{"href":4573,"rel":4574},"https:\u002F\u002Fgithub.com\u002Fnirgn975\u002Fresume",[62],"source code",[26,4577,4578,4579,126],{},"I call the theme ",[871,4580,4581],{},[58,4582,4585],{"href":4583,"rel":4584},"https:\u002F\u002Fgithub.com\u002Fnirgn975\u002FdevRes",[62],"devRes",[26,4587,4588],{},[41,4589],{"alt":4590,"className":4591,"src":4592},"devRes Mockups",[45,46],"\u002Fposts\u002F2021\u002Fhugo-theme\u002Fdevres-mockups.webp",[26,4594,4595,4596,4601],{},"To see this theme in action (without my own content), here is a ",[58,4597,4600],{"href":4598,"rel":4599},"https:\u002F\u002Fnirgn975.github.io\u002FdevRes",[62],"live demo"," site which is rendered with this theme and some content for documentation.",[26,4603,4604],{},"There are a lot of other great features that this theme has, like:",[151,4606,4607,4610,4613,4616,4619,4633,4647,4656,4659],{},[154,4608,4609],{},"Responsive layout.",[154,4611,4612],{},"Light \u002F Dark mode.",[154,4614,4615],{},"Generate GitHub project cards automatically from a repo name.",[154,4617,4618],{},"Automagically get the latest posts from your blog.",[154,4620,4621,4622,1894,4627,4632],{},"Use ",[58,4623,4626],{"href":4624,"rel":4625},"https:\u002F\u002Ffontawesome.com",[62],"Font Awesome",[58,4628,4631],{"href":4629,"rel":4630},"https:\u002F\u002Fdevicon.dev",[62],"Devicons"," icons.",[154,4634,4635,4636,120,4641,4646],{},"Support ",[58,4637,4640],{"href":4638,"rel":4639},"https:\u002F\u002Fanalytics.google.com\u002Fanalytics",[62],"Google",[58,4642,4645],{"href":4643,"rel":4644},"https:\u002F\u002Fplausible.io",[62],"Plausible"," analytics.",[154,4648,4649,4650,4655],{},"Optimized for Performance, Accessibility, Best Practices, and SEO: 90+ (out of 100) on mobile and desktop in ",[58,4651,4654],{"href":4652,"rel":4653},"https:\u002F\u002Fdevelopers.google.com\u002Fspeed\u002Fpagespeed\u002Finsights",[62],"Google PageSpeed Insights"," (lighthouse).",[154,4657,4658],{},"CDN for all third-party libraries supported.",[154,4660,4661,4662,120,4667,126],{},"Automated contact form with ",[58,4663,4666],{"href":4664,"rel":4665},"https:\u002F\u002Fformspree.io",[62],"formspree",[58,4668,4671],{"href":4669,"rel":4670},"https:\u002F\u002Fgetform.io",[62],"getform",[458,4673,4675],{"id":4674},"_2-getting-started","2. Getting started",[26,4677,4678,4679,4682],{},"If you want to use this theme, it can't get any easier than this. All you need to do is to install ",[58,4680,4553],{"href":4551,"rel":4681},[62],", create a new website, and clone this theme as a submodule",[242,4684,4686],{"className":886,"code":4685,"filename":888,"language":889,"meta":188,"style":188},"brew install hugo\nhugo new site myownresume\ngit submodule add git@github.com:nirgn975\u002FdevRes.git themes\u002FdevRes\n",[249,4687,4688,4698,4712],{"__ignoreMap":188},[252,4689,4690,4693,4695],{"class":254,"line":255},[252,4691,4692],{"class":896},"brew",[252,4694,1175],{"class":900},[252,4696,4697],{"class":900}," hugo\n",[252,4699,4700,4703,4706,4709],{"class":254,"line":189},[252,4701,4702],{"class":896},"hugo",[252,4704,4705],{"class":900}," new",[252,4707,4708],{"class":900}," site",[252,4710,4711],{"class":900}," myownresume\n",[252,4713,4714,4716,4719,4722,4725],{"class":254,"line":326},[252,4715,897],{"class":896},[252,4717,4718],{"class":900}," submodule",[252,4720,4721],{"class":900}," add",[252,4723,4724],{"class":900}," git@github.com:nirgn975\u002FdevRes.git",[252,4726,4727],{"class":900}," themes\u002FdevRes\n",[26,4729,4730],{},"Now all you have left to do is just config it as your theme",[242,4732,4737],{"className":4733,"code":4734,"filename":4735,"language":4736,"meta":188,"style":188},"language-toml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","theme = \"devRes\"\n","config.toml","toml",[249,4738,4739],{"__ignoreMap":188},[252,4740,4741],{"class":254,"line":255},[252,4742,4734],{},[26,4744,4745,4746,4751,4752,4754,4755,4758],{},"You can look at the ",[58,4747,4750],{"href":4748,"rel":4749},"https:\u002F\u002Fgithub.com\u002Fnirgn975\u002FdevRes\u002Ftree\u002Fmain\u002FexampleSite",[62],"exampleSite"," to check out the ",[249,4753,4735],{}," and the ",[249,4756,4757],{},"data\u002Fcontent.yaml"," for examples.",[4760,4761,4763],"h3",{"id":4762},"_21-customization","2.1 Customization",[26,4765,4766,4767,4769],{},"Your resume content should be added in the ",[249,4768,4757],{}," file.",[26,4771,4772,4773,4769],{},"If you want to change the theme colors, just do it in the ",[249,4774,4735],{},[26,4776,4777,4778,4781,4782,4785,4786,4791,4792,120,4795,126],{},"And lastly your favicons should go in an ",[249,4779,4780],{},"icons"," directory inside ",[249,4783,4784],{},"static",". You can easily generate them with ",[58,4787,4790],{"href":4788,"rel":4789},"https:\u002F\u002Frealfavicongenerator.net",[62],"realfavicongenerator",", and set theme-color and background-color in ",[249,4793,4794],{},"browserconfig.xml",[249,4796,4797],{},"site.webmanifest",[4760,4799,4801],{"id":4800},"_22-want-to-help","2.2 Want to help?",[26,4803,4804],{},"Great! All issues and pull requests are welcome.",[26,4806,4807,4808,4811,4812,4814],{},"For local development just start ",[58,4809,4702],{"href":4551,"rel":4810},[62]," with ",[249,4813,4750],{}," as the source.",[242,4816,4818],{"className":886,"code":4817,"filename":888,"language":889,"meta":188,"style":188},"hugo server --source=exampleSite -v --gc\n",[249,4819,4820],{"__ignoreMap":188},[252,4821,4822,4824,4827,4830,4833],{"class":254,"line":255},[252,4823,4702],{"class":896},[252,4825,4826],{"class":900}," server",[252,4828,4829],{"class":900}," --source=exampleSite",[252,4831,4832],{"class":900}," -v",[252,4834,4835],{"class":900}," --gc\n",[458,4837,4839],{"id":4838},"_3-summary","3. Summary",[26,4841,4842],{},"The project will get into the showcase at Hugo - an issue has been opened and all the checks are green.",[26,4844,4845,4846,126],{},"I really hope you find it useful and if you ever use it or just play with the theme, I’ll be thrilled to get a link or just hear from you. If you have any issues or any questions feel free to open a ",[58,4847,4850],{"href":4848,"rel":4849},"https:\u002F\u002Fgithub.com\u002Fnirgn975\u002FdevRes\u002Fdiscussions",[62],"new discussion at GitHub",[772,4852,4853],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":188,"searchDepth":189,"depth":189,"links":4855},[4856,4857,4861],{"id":4560,"depth":189,"text":4561},{"id":4674,"depth":189,"text":4675,"children":4858},[4859,4860],{"id":4762,"depth":326,"text":4763},{"id":4800,"depth":326,"text":4801},{"id":4838,"depth":189,"text":4839},"2021-01-01","Introducing devRes, a developer-focused Hugo resume theme.",{"src":4865},"\u002Fposts\u002F2021\u002Fhugo-theme\u002Fhugo-logo-wide.webp",{},"\u002Fblog\u002F2021\u002Fhugo-theme",{"title":4524,"description":4863},"blog\u002F2021\u002Fhugo-theme","X5vBL1udp2GYY8rHNgYcNU_K8j1_eQt4W9EET9RgqLI",{"id":4872,"title":4873,"author":4874,"badge":4876,"body":4877,"date":5554,"description":5555,"draft":193,"extension":194,"image":5556,"meta":5558,"navigation":198,"path":5570,"seo":5571,"stem":5572,"__hash__":5573},"posts\u002Fblog\u002F2020\u002Freleasing-software-is-hard.md","Releasing Software Is Hard! Is it?",{"name":16,"to":17,"avatar":4875},{"src":19},{"label":796},{"type":23,"value":4878,"toc":5546},[4879,4882,4885,4893,4897,4906,4909,4918,4951,4958,4962,4972,4987,5000,5007,5020,5023,5027,5036,5049,5056,5081,5085,5101,5147,5150,5157,5167,5199,5213,5225,5232,5236,5243,5250,5272,5293,5434,5437,5469,5476,5483,5494,5501,5508,5515,5517,5535,5543],[26,4880,4881],{},"One of the things I always got frustrated with is software releases and versioning.",[26,4883,4884],{},"In the old days, back when I started to work at my first job as a software engineer my team leader handled all of the project management. It means he knew what issue you're working on at the moment, and when (in what version of the software) it should be merged and be released. To this day I think the versioning was just a random thing there - doesn't mean anything except for all of us to be on the same page when we talked about it or reference it.",[26,4886,4887,4888,126],{},"Bugs and features were pushed forward to next versions without any real systematic reasoning about it, and I knew something about it was off. That's when I opened up my browser and just started googling about the topic. I searched and read a lot, and finally I ran into ",[58,4889,4892],{"href":4890,"rel":4891},"https:\u002F\u002Fsemver.org",[62],"Semantic Versioning",[458,4894,4896],{"id":4895},"_1-semantic-versioning","1. Semantic Versioning",[26,4898,4899,4900,4905],{},"Semantic Versioning (or ",[58,4901,4904],{"href":4902,"rel":4903},"https:\u002F\u002Fgithub.com\u002Fsemver\u002Fsemver",[62],"semver",") is basically a simple set of rules and requirements that dictate how version numbers are assigned and incremented. In practice SemVer is probably similar to what you're already doing right now, but similar is worthless. Because without compliance to some sort of formal specification, version numbers are essentially useless for dependency management.",[26,4907,4908],{},"Semantic Versioning was authored back in 2011 and it's already widely used by the open source community. All the major open source projects embraced it, so it's at least worth reading the specification. Once you follow the semver specification your version numbers and the way they change convey meaning about the underlying code and what has been modified from one version to the next.",[26,4910,4911,4912,4917],{},"If that's not convincing you maybe the fact that the Semantic Versioning specification was originally authored by ",[58,4913,4916],{"href":4914,"rel":4915},"https:\u002F\u002Fgithub.com\u002Fmojombo",[62],"Tom Preston-Werner"," (cofounder of GitHub) will.",[26,4919,4920,4921,2279,4924,4927,4928,4931,4932,4935,4936,4931,4939,4942,4943,4946,4947,4950],{},"So, if you're still reading this post I assume you're somewhat interested in semver. So go read the specification, it's short, concise, and translated to a lot of languages. I'll just mention the basics: A version format is ",[249,4922,4923],{},"X.Y.Z",[249,4925,4926],{},"Major.Minor.Patch"," respectively). The ",[249,4929,4930],{},"Major"," is incremented if the API additions\u002Fchanges are ",[871,4933,4934],{},"backwards incompatible"," (i.e. breaking changes). The ",[249,4937,4938],{},"Minor",[871,4940,4941],{},"backwards compatible",". And the ",[249,4944,4945],{},"Patch"," is incremented if a bug fix ",[871,4948,4949],{},"not affecting"," the API at all.",[26,4952,4953],{},[41,4954],{"alt":4955,"className":4956,"src":4957},"Semantic Versioning Summary",[45,46],"\u002Fposts\u002F2020\u002Freleasing-software-is-hard\u002Fsemver-summary.webp",[458,4959,4961],{"id":4960},"_2-sounds-great-but","2. Sounds Great, but..",[26,4963,4964,4965,4968,4969,4971],{},"Semantic Versioning sounds great, but it presents a new problem. Every release someone has to sit down, read the issues\u002Fpull requests and write a ",[249,4966,4967],{},"changelog"," for that version, from that ",[249,4970,4967],{}," he should bump the version of the software accordingly. This sounds like a really tedious manual job. This alone kept me from really using SemVer for a long time.",[26,4973,4974,4975,4978,4979,4981,4982,4986],{},"Until I found ",[58,4976,3491],{"href":3489,"rel":4977},[62],"! Which is a tool to automate the whole package release workflow (determining the next version number, generating the release notes - ",[249,4980,4967],{},", and publishing the package). This removes the immediate connection between human emotions and version numbers, strictly following the ",[58,4983,4892],{"href":4984,"rel":4985},"http:\u002F\u002Fsemver.org",[62]," specification.",[26,4988,4989,4990,4995,4996,4999],{},"To make our life even simpler we can use the ",[58,4991,4994],{"href":4992,"rel":4993},"https:\u002F\u002Fgithub.com\u002Fmarketplace\u002Factions\u002Frelease-me#create-a-release",[62],"Release me!"," GitHub action which uses ",[58,4997,3491],{"href":3489,"rel":4998},[62]," under the hood.",[26,5001,5002],{},[41,5003],{"alt":5004,"className":5005,"src":5006},"Automate All The Things",[45,46],"\u002Fposts\u002F2020\u002Freleasing-software-is-hard\u002Fautomate-all-the-things.webp",[26,5008,5009,5010,5015,5016,5019],{},"But how semantic-release knows what part of the version it should bump? It uses the commit messages to determine the type of changes in the codebase. So that means we need to adhere to a specific commit message format. By default the semantic-release follows the ",[58,5011,5014],{"href":5012,"rel":5013},"https:\u002F\u002Fgithub.com\u002Fangular\u002Fangular.js\u002Fblob\u002Fmaster\u002FDEVELOPERS.md#-git-commit-guidelines",[62],"Angular Commit Message Conventions",", but you can change it with a ",[249,5017,5018],{},"config"," file. We'll not change it for this post because this format is widely used by the open source community.",[26,5021,5022],{},"I'm starting to get a feeling here we're just kicking the ball to different places and don't really have a solution to the problem. Because now we need to learn and manually write our commit messages in a specific format.",[458,5024,5026],{"id":5025},"_3-lets-solve-this-once-and-for-all","3. Let's solve this once and for all",[26,5028,5029,5030,5035],{},"You're sort of right. Our problem is still present, but those other problems were still present before we talked about them, we just didn't mention them. But fear not, ",[58,5031,5034],{"href":5032,"rel":5033},"https:\u002F\u002Fwww.conventionalcommits.org",[62],"Conventional Commits"," to the rescue. The Conventional Commits specification is a convention on top of commit messages that provides a set of rules for creating an explicit commit history. And I know what you're thinking to yourself right now \"another specification?!\".",[26,5037,5038,5039,5044,5045,5048],{},"Again, fear not my young padawan. ",[58,5040,5043],{"href":5041,"rel":5042},"https:\u002F\u002Fgithub.com\u002Fcommitizen\u002Fcz-cli",[62],"commitizen"," - a command line utility will help us ",[249,5046,5047],{},"commit"," and will guide us through every commit.",[26,5050,5051],{},[41,5052],{"alt":5053,"className":5054,"src":5055},"Commitizen In Action",[45,46],"\u002Fposts\u002F2020\u002Freleasing-software-is-hard\u002Fcommitizen-in-action.webp",[26,5057,5058,5059,5062,5063,5065,5066,120,5069,5072,5073,5076,5077,5080],{},"So, let's recap all the tools so far. If we use ",[58,5060,5043],{"href":5041,"rel":5061},[62]," for ",[249,5064,5047],{},"s, our commit messages will be in a format compatible with ",[58,5067,5014],{"href":5012,"rel":5068},[62],[58,5070,5034],{"href":5032,"rel":5071},[62],". If our commits are compatible with those formats ",[58,5074,3491],{"href":3489,"rel":5075},[62]," will determine the next version number, generate the release notes, and publish the package by the ",[58,5078,4892],{"href":4890,"rel":5079},[62]," specification. Perfect!",[458,5082,5084],{"id":5083},"_4-time-to-get-our-hands-dirty","4. Time to get our hands dirty",[26,5086,5087,5088,5090,5091,5094,5095,5100],{},"Let's start by creating a simple ",[249,5089,3033],{}," file with npm in our project directory (let's call it ",[249,5092,5093],{},"deweb"," because that's what the ",[58,5096,5099],{"href":5097,"rel":5098},"https:\u002F\u002Fnamelix.com",[62],"namelix"," generator generated for us).",[242,5102,5104],{"className":886,"code":5103,"filename":888,"language":889,"meta":188,"style":188},"mkdir deweb\ncd deweb\ngit init\nnpm init\necho node_modules >> .gitignore\n",[249,5105,5106,5114,5120,5127,5133],{"__ignoreMap":188},[252,5107,5108,5111],{"class":254,"line":255},[252,5109,5110],{"class":896},"mkdir",[252,5112,5113],{"class":900}," deweb\n",[252,5115,5116,5118],{"class":254,"line":189},[252,5117,1103],{"class":296},[252,5119,5113],{"class":900},[252,5121,5122,5124],{"class":254,"line":326},[252,5123,897],{"class":896},[252,5125,5126],{"class":900}," init\n",[252,5128,5129,5131],{"class":254,"line":343},[252,5130,1077],{"class":896},[252,5132,5126],{"class":900},[252,5134,5135,5138,5141,5144],{"class":254,"line":354},[252,5136,5137],{"class":296},"echo",[252,5139,5140],{"class":900}," node_modules",[252,5142,5143],{"class":262}," >>",[252,5145,5146],{"class":900}," .gitignore\n",[26,5148,5149],{},"Here are my configurations.",[26,5151,5152],{},[41,5153],{"alt":5154,"className":5155,"src":5156},"Init The Project",[45,46],"\u002Fposts\u002F2020\u002Freleasing-software-is-hard\u002Finit-the-project.webp",[26,5158,5159,5160,5163,5164],{},"Now we need to install ",[58,5161,5043],{"href":5041,"rel":5162},[62]," and make our repo ",[871,5165,5166],{},"commitizen friendly",[242,5168,5170],{"className":886,"code":5169,"filename":888,"language":889,"meta":188,"style":188},"npm install commitizen -g\ncommitizen init cz-conventional-changelog --save-dev --save-exact\n",[249,5171,5172,5184],{"__ignoreMap":188},[252,5173,5174,5176,5178,5181],{"class":254,"line":255},[252,5175,1077],{"class":896},[252,5177,1175],{"class":900},[252,5179,5180],{"class":900}," commitizen",[252,5182,5183],{"class":900}," -g\n",[252,5185,5186,5188,5190,5193,5196],{"class":254,"line":189},[252,5187,5043],{"class":896},[252,5189,1080],{"class":900},[252,5191,5192],{"class":900}," cz-conventional-changelog",[252,5194,5195],{"class":900}," --save-dev",[252,5197,5198],{"class":900}," --save-exact\n",[26,5200,5201,5202,5205,5206,5209,5210,5212],{},"This just tells commitizen which adapter we actually want our contributors to use when they try to commit to this repo. And now you can use ",[249,5203,5204],{},"git cz"," command instead of ",[249,5207,5208],{},"git commit"," and the commitizen prompt will guide you through the commit message and ",[249,5211,5047],{}," all the files.",[242,5214,5216],{"className":886,"code":5215,"filename":888,"language":889,"meta":188,"style":188},"git cz\n",[249,5217,5218],{"__ignoreMap":188},[252,5219,5220,5222],{"class":254,"line":255},[252,5221,897],{"class":896},[252,5223,5224],{"class":900}," cz\n",[26,5226,5227],{},[41,5228],{"alt":5229,"className":5230,"src":5231},"Commit With Commitizen",[45,46],"\u002Fposts\u002F2020\u002Freleasing-software-is-hard\u002Fcommit-with-commitizen.webp",[458,5233,5235],{"id":5234},"_5-release-the-damn-thing","5. Release the damn thing",[26,5237,5238,5239,5242],{},"Now, we can use ",[58,5240,3491],{"href":3489,"rel":5241},[62]," manually to automatically generate the release (I mean to write the command by hand every time we want to create a new release). But we can do it even better! Why not do it automagically in GitHub actions, and even tie it up to GitHub milestones.",[26,5244,5245,5246,5249],{},"So, let's create a GitHub action that uses the ",[58,5247,4994],{"href":4992,"rel":5248},[62]," action and runs every time there is a milestone event on GitHub.",[242,5251,5253],{"className":886,"code":5252,"filename":888,"language":889,"meta":188,"style":188},"mkdir -p .github\u002Fworkflows\ntouch .github\u002Fworkflows\u002Fcd.yml\n",[249,5254,5255,5265],{"__ignoreMap":188},[252,5256,5257,5259,5262],{"class":254,"line":255},[252,5258,5110],{"class":896},[252,5260,5261],{"class":900}," -p",[252,5263,5264],{"class":900}," .github\u002Fworkflows\n",[252,5266,5267,5269],{"class":254,"line":189},[252,5268,1869],{"class":896},[252,5270,5271],{"class":900}," .github\u002Fworkflows\u002Fcd.yml\n",[26,5273,5274,5275,5280,5281,5285,5286,5288,5289,5292],{},"In this file you should copy the default template of a ",[58,5276,5279],{"href":5277,"rel":5278},"https:\u002F\u002Fdocs.github.com\u002Fen\u002Ffree-pro-team@latest\u002Factions",[62],"GitHub action",", and use the ",[58,5282,5284],{"href":4992,"rel":5283},[62],"release-me-action",". Then you need to change the ",[249,5287,3550],{}," key on the top of the file to ",[249,5290,5291],{},"milestone"," and only trigger when a milestone is closed.",[242,5294,5297],{"className":3526,"code":5295,"filename":5296,"language":3529,"meta":188,"style":188},"name: Continuous Deployment\n\non:\n  milestone:\n    types: [closed]\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v2\n\n      - name: Create Release 🚀\n        uses: ridedott\u002Frelease-me-action@master\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          release-branches: '[\"main\"]'\n",".github\u002Fworkflows\u002Fcd.yml",[249,5298,5299,5307,5311,5317,5324,5338,5342,5348,5355,5363,5369,5380,5384,5394,5402,5408,5416,5422],{"__ignoreMap":188},[252,5300,5301,5303,5305],{"class":254,"line":255},[252,5302,3536],{"class":308},[252,5304,312],{"class":262},[252,5306,3541],{"class":900},[252,5308,5309],{"class":254,"line":189},[252,5310,1695],{"emptyLinePlaceholder":198},[252,5312,5313,5315],{"class":254,"line":326},[252,5314,3550],{"class":2415},[252,5316,3553],{"class":262},[252,5318,5319,5322],{"class":254,"line":343},[252,5320,5321],{"class":308},"  milestone",[252,5323,3553],{"class":262},[252,5325,5326,5329,5331,5333,5336],{"class":254,"line":354},[252,5327,5328],{"class":308},"    types",[252,5330,312],{"class":262},[252,5332,1263],{"class":262},[252,5334,5335],{"class":900},"closed",[252,5337,3476],{"class":262},[252,5339,5340],{"class":254,"line":364},[252,5341,1695],{"emptyLinePlaceholder":198},[252,5343,5344,5346],{"class":254,"line":381},[252,5345,3584],{"class":308},[252,5347,3553],{"class":262},[252,5349,5350,5353],{"class":254,"line":398},[252,5351,5352],{"class":308},"  deploy",[252,5354,3553],{"class":262},[252,5356,5357,5359,5361],{"class":254,"line":415},[252,5358,3598],{"class":308},[252,5360,312],{"class":262},[252,5362,3603],{"class":900},[252,5364,5365,5367],{"class":254,"line":421},[252,5366,3621],{"class":308},[252,5368,3553],{"class":262},[252,5370,5371,5373,5375,5377],{"class":254,"line":427},[252,5372,3572],{"class":262},[252,5374,3630],{"class":308},[252,5376,312],{"class":262},[252,5378,5379],{"class":900}," actions\u002Fcheckout@v2\n",[252,5381,5382],{"class":254,"line":662},[252,5383,1695],{"emptyLinePlaceholder":198},[252,5385,5386,5388,5390,5392],{"class":254,"line":668},[252,5387,3572],{"class":262},[252,5389,3646],{"class":308},[252,5391,312],{"class":262},[252,5393,3819],{"class":900},[252,5395,5396,5398,5400],{"class":254,"line":674},[252,5397,3656],{"class":308},[252,5399,312],{"class":262},[252,5401,3829],{"class":900},[252,5403,5404,5406],{"class":254,"line":1414},[252,5405,3835],{"class":308},[252,5407,3553],{"class":262},[252,5409,5410,5412,5414],{"class":254,"line":1610},[252,5411,3843],{"class":308},[252,5413,312],{"class":262},[252,5415,3848],{"class":900},[252,5417,5418,5420],{"class":254,"line":1618},[252,5419,3666],{"class":308},[252,5421,3553],{"class":262},[252,5423,5424,5426,5428,5430,5432],{"class":254,"line":1628},[252,5425,3861],{"class":308},[252,5427,312],{"class":262},[252,5429,910],{"class":262},[252,5431,3868],{"class":900},[252,5433,3871],{"class":262},[26,5435,5436],{},"Now let's commit the new file and push it to GitHub.",[242,5438,5440],{"className":886,"code":5439,"filename":888,"language":889,"meta":188,"style":188},"git add .\ngit cz\ngit push origin main\n",[249,5441,5442,5451,5457],{"__ignoreMap":188},[252,5443,5444,5446,5448],{"class":254,"line":255},[252,5445,897],{"class":896},[252,5447,4721],{"class":900},[252,5449,5450],{"class":900}," .\n",[252,5452,5453,5455],{"class":254,"line":189},[252,5454,897],{"class":896},[252,5456,5224],{"class":900},[252,5458,5459,5461,5464,5467],{"class":254,"line":326},[252,5460,897],{"class":896},[252,5462,5463],{"class":900}," push",[252,5465,5466],{"class":900}," origin",[252,5468,3575],{"class":900},[26,5470,5471,5472,5475],{},"The last thing we need to do is to create a milestone in GitHub, then look at the ",[871,5473,5474],{},"Actions"," section and see our workflow, but we notice it doesn't run yet.",[26,5477,5478],{},[41,5479],{"alt":5480,"className":5481,"src":5482},"GitHub Actions When Milestone Is Open",[45,46],"\u002Fposts\u002F2020\u002Freleasing-software-is-hard\u002Fgithub-actions-when-milestone-is-open.webp",[26,5484,5485,5486,5489,5490,5493],{},"Then let's close the milestone (just hit the ",[249,5487,5488],{},"close"," button), and refresh the ",[871,5491,5492],{},"Action"," tab.",[26,5495,5496],{},[41,5497],{"alt":5498,"className":5499,"src":5500},"GitHub Actions After Milestone Closed",[45,46],"\u002Fposts\u002F2020\u002Freleasing-software-is-hard\u002Fgithub-actions-after-milestone-closed.webp",[26,5502,5503,5504,5507],{},"And when the workflow finishes running, let's open the releases page and see our new release (you can notice the action also created a ",[249,5505,5506],{},"changelog.md"," file with all the changes, and it'll update this file with every release).",[26,5509,5510],{},[41,5511],{"alt":5512,"className":5513,"src":5514},"GitHub Releases After Action Completed",[45,46],"\u002Fposts\u002F2020\u002Freleasing-software-is-hard\u002Fgithub-releases-after-action-completed.webp",[458,5516,4495],{"id":4494},[26,5518,5519,5520,5522,5523,5526,5527,5530,5531,5534],{},"To recap, we now release our software automatically without any human intervention except to ",[249,5521,5488],{}," the milestone (we can easily change it on the GitHub workflow). Our commit messages are nicely organized with ",[58,5524,5043],{"href":5041,"rel":5525},[62]," by the ",[58,5528,5034],{"href":5032,"rel":5529},[62]," specification. Our release happens automatically with GitHub actions. And the release itself is generated with ",[58,5532,3491],{"href":3489,"rel":5533},[62]," - the next version number, the release notes and the publishing itself to GitHub releases.",[26,5536,5537,5538,5542],{},"If you want to see it all happen or maybe got stuck along the way, just go over to the ",[58,5539,5093],{"href":5540,"rel":5541},"https:\u002F\u002Fgithub.com\u002Fnirgn975\u002Fdeweb",[62]," repo on GitHub and check out the code.",[772,5544,5545],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":188,"searchDepth":189,"depth":189,"links":5547},[5548,5549,5550,5551,5552,5553],{"id":4895,"depth":189,"text":4896},{"id":4960,"depth":189,"text":4961},{"id":5025,"depth":189,"text":5026},{"id":5083,"depth":189,"text":5084},{"id":5234,"depth":189,"text":5235},{"id":4494,"depth":189,"text":4495},"2020-12-01","Automating versioning and releases with Semantic Versioning and semantic-release.",{"src":5557},"\u002Fposts\u002F2020\u002Freleasing-software-is-hard\u002Fsoftware-development.webp",{"excerpt":5559},{"type":23,"value":5560},[5561,5563,5565],[26,5562,4881],{},[26,5564,4884],{},[26,5566,4887,5567,126],{},[58,5568,4892],{"href":4890,"rel":5569},[62],"\u002Fblog\u002F2020\u002Freleasing-software-is-hard",{"title":4873,"description":5555},"blog\u002F2020\u002Freleasing-software-is-hard","2RF-OkKXWA0LRhRRXQP87RuYZp3iGp5xWfhhFCJK4G8",{"id":5575,"title":5576,"author":5577,"badge":5579,"body":5581,"date":6290,"description":6291,"draft":193,"extension":194,"image":6292,"meta":6294,"navigation":198,"path":6295,"seo":6296,"stem":6297,"__hash__":6298},"posts\u002Fblog\u002F2020\u002Fcrack-the-hash.md","Crack The Hash",{"name":16,"to":17,"avatar":5578},{"src":19},{"label":5580},"hacking",{"type":23,"value":5582,"toc":6283},[5583,5597,5605,5609,5622,5631,5638,5663,5667,5678,5687,5700,5715,5726,5729,5738,5745,5749,5764,5771,5780,5789,5870,5876,5976,5982,5991,5995,5998,6013,6025,6028,6037,6064,6080,6099,6129,6138,6147,6172,6181,6190,6197,6227,6234,6263,6275,6277,6280],[26,5584,5585,5586,5590,5591,5596],{},"In earlier post (at ",[58,5587,5589],{"href":5588},"\u002Fblog\u002F2020\u002Fopen-source-intelligence\u002F#6-pivoting","Passive.. Passive Recon.. Passive Reconnaissance.. OSINT!",") I mention we can use ",[58,5592,5595],{"href":5593,"rel":5594},"https:\u002F\u002Fhashcat.net",[62],"hashcat"," to try and crack a password we found, but it wasn't the meaning of the post (and it's a red line for me to do that and put his cleartext password on the web for someone who didn't actually try to hack my service).",[26,5598,5599,5600,983],{},"But in this post we'll learn how to use hashcat to crack passwords, and even do that much faster on the cloud with GCP (",[58,5601,5604],{"href":5602,"rel":5603},"https:\u002F\u002Fcloud.google.com\u002F",[62],"Google Cloud Platform",[458,5606,5608],{"id":5607},"_1-request-a-quota","1. Request a Quota",[26,5610,5611,5612,5615,5616,5621],{},"First thing you need to do is to request a quota, because by default the quota for GPU on GCP is zero. In this post I'll be using ",[951,5613,5614],{},"\"\"NVIDIA Tesla K80\""," GPU, so go to ",[58,5617,5620],{"href":5618,"rel":5619},"https:\u002F\u002Fcloud.google.com\u002Fcompute\u002Fdocs\u002Fgpus\u002F#introduction",[62],"this"," link and make sure they have this kind of GPU on the zone you want to deploy your VM.",[26,5623,5624],{},[41,5625],{"alt":5626,"className":5627,"height":5628,"src":5629,"width":5630},"Available GPU Zones",[45,46],350,"\u002Fposts\u002F2020\u002Fcrack-the-hash\u002Favailable-gpu-zones.webp",841,[26,5632,5633,5634,5637],{},"In my case I want to deploy the VM on ",[249,5635,5636],{},"europe-west1-b",", so we're clear.",[26,5639,5640,5641,5644,5645,5648,5649,5652,5653,5655,5656,5659,5660,126],{},"To make the quota request open the left menu and under ",[951,5642,5643],{},"\"IAM & Admin\""," you'll have a ",[951,5646,5647],{},"\"Quotas\""," button, click it and just filter by the ",[951,5650,5651],{},"\"GPU\""," keyword to see all the GPUs GCP offer. Choose ",[951,5654,5614],{},", and click on ",[951,5657,5658],{},"\"ALL QUOTAS\"",", now just mark the zone you want your quota in and click on the top button that says ",[951,5661,5662],{},"\"EDIT QUOTAS\"",[458,5664,5666],{"id":5665},"_2-create-the-vm","2. Create the VM",[26,5668,5669,5670,5673,5674,5677],{},"After Google approved your quota, let's build the VM. Go to ",[951,5671,5672],{},"\"Compute Engine\""," tab on the left side menu in GCP, and then click on the ",[951,5675,5676],{},"\"CREATE INSTANCE\""," button.",[26,5679,5680],{},[41,5681],{"alt":5682,"className":5683,"height":5684,"src":5685,"width":5686},"GCP Compute Engine",[45,46],317,"\u002Fposts\u002F2020\u002Fcrack-the-hash\u002Fgcp-compute-engine.webp",830,[26,5688,5689,5690,5693,5694,5697,5698,983],{},"Now you'll be redirected to a new page where you can configure your VM. I named my VM ",[249,5691,5692],{},"crack-the-hash"," and then I choose the region I got the quota at (in my case ",[249,5695,5696],{},"Belgium"," and the zone ",[249,5699,5636],{},[26,5701,5702,5703,5706,5707,5710,5711,5714],{},"The next step is to choose the machine type. In our case we need to choose the ",[951,5704,5705],{},"\"old\""," series, ",[249,5708,5709],{},"N1",", because this is the only one that has the option for GPU. After that, choose the machine type itself (I choose the ",[249,5712,5713],{},"n1-standard-4"," which has 4 vCPU and 15Gb memory, but you can choose which ever you want. Just keep in mind it'll affect the speed a bit).",[26,5716,5717,5718,5721,5722,5725],{},"The GPU is kinda hidden under the ",[249,5719,5720],{},"machine configuration"," section, you'll see a ",[249,5723,5724],{},"CPU platform and GPU"," button, click it and an option to choose a GPU will open up. I choose Nvidia Tesla K80, because we're just learning here and I want to keep the costs down",[26,5727,5728],{},"The final step is the boot disk, I choose 30GB SSD and the latest Ubuntu (20.04) LTS - which means we'll install CUDA manually (Google has images with CUDA installed by default, but we're trying to learn here so I choose the longer way).",[26,5730,5731],{},[41,5732],{"alt":5733,"className":5734,"height":5735,"src":5736,"width":5737},"GCP Instance Configuration",[45,46],578,"\u002Fposts\u002F2020\u002Fcrack-the-hash\u002Fgcp-instance-configuration.webp",603,[26,5739,5740,5741,5744],{},"After you're all done, scroll down and click on the ",[951,5742,5743],{},"\"Create\""," blue button and just wait a couple of minutes until the machine will be created and boot up.",[458,5746,5748],{"id":5747},"_3-install-cuda-and-hashcat","3. Install CUDA and Hashcat",[26,5750,5751,5752,5757,5758,5763],{},"When the machine is up and running the first thing we need to do is to install ",[58,5753,5756],{"href":5754,"rel":5755},"https:\u002F\u002Fdeveloper.nvidia.com\u002Fabout-cuda",[62],"CUDA"," first. CUDA is a parallel computing platform and application programming interface model created by Nvidia and ",[58,5759,5762],{"href":5760,"rel":5761},"https:\u002F\u002Fhashcat.net\u002Fhashcat",[62],"Hashcat"," is using it.",[26,5765,5766,5767,5770],{},"First we need to SSH into the machine, the easiest way to do it is to click on the ",[951,5768,5769],{},"\"SSH\""," button and just let GCP open a new terminal right in the browser window.",[26,5772,5773],{},[41,5774],{"alt":5775,"className":5776,"height":5777,"src":5778,"width":5779},"GCP Connect Via SSH",[45,46],153,"\u002Fposts\u002F2020\u002Fcrack-the-hash\u002Fgcp-connect-via-ssh.webp",682,[26,5781,5782,5783,5788],{},"After that the terminal is open, go to the ",[58,5784,5787],{"href":5785,"rel":5786},"https:\u002F\u002Fdeveloper.nvidia.com\u002Fcuda-downloads?target_os=Linux&target_arch=x86_64&target_distro=Ubuntu&target_version=2004&target_type=debnetwork",[62],"CUDA Toolkit download page"," and choose the platform you want to install it to, it'll give you all the commands - just paste them in the terminal. In my case here they are:",[242,5790,5792],{"className":886,"code":5791,"filename":888,"language":889,"meta":188,"style":188},"wget https:\u002F\u002Fdeveloper.download.nvidia.com\u002Fcompute\u002Fcuda\u002Frepos\u002Fubuntu2004\u002Fx86_64\u002Fcuda-ubuntu2004.pin\nsudo mv cuda-ubuntu2004.pin \u002Fetc\u002Fapt\u002Fpreferences.d\u002Fcuda-repository-pin-600\nsudo apt-key adv --fetch-keys https:\u002F\u002Fdeveloper.download.nvidia.com\u002Fcompute\u002Fcuda\u002Frepos\u002Fubuntu2004\u002Fx86_64\u002F7fa2af80.pub\nsudo add-apt-repository \"deb https:\u002F\u002Fdeveloper.download.nvidia.com\u002Fcompute\u002Fcuda\u002Frepos\u002Fubuntu2004\u002Fx86_64\u002F \u002F\"\nsudo apt-get update\nsudo apt-get -y install cuda\n",[249,5793,5794,5802,5816,5832,5846,5856],{"__ignoreMap":188},[252,5795,5796,5799],{"class":254,"line":255},[252,5797,5798],{"class":896},"wget",[252,5800,5801],{"class":900}," https:\u002F\u002Fdeveloper.download.nvidia.com\u002Fcompute\u002Fcuda\u002Frepos\u002Fubuntu2004\u002Fx86_64\u002Fcuda-ubuntu2004.pin\n",[252,5803,5804,5807,5810,5813],{"class":254,"line":189},[252,5805,5806],{"class":896},"sudo",[252,5808,5809],{"class":900}," mv",[252,5811,5812],{"class":900}," cuda-ubuntu2004.pin",[252,5814,5815],{"class":900}," \u002Fetc\u002Fapt\u002Fpreferences.d\u002Fcuda-repository-pin-600\n",[252,5817,5818,5820,5823,5826,5829],{"class":254,"line":326},[252,5819,5806],{"class":896},[252,5821,5822],{"class":900}," apt-key",[252,5824,5825],{"class":900}," adv",[252,5827,5828],{"class":900}," --fetch-keys",[252,5830,5831],{"class":900}," https:\u002F\u002Fdeveloper.download.nvidia.com\u002Fcompute\u002Fcuda\u002Frepos\u002Fubuntu2004\u002Fx86_64\u002F7fa2af80.pub\n",[252,5833,5834,5836,5839,5841,5844],{"class":254,"line":343},[252,5835,5806],{"class":896},[252,5837,5838],{"class":900}," add-apt-repository",[252,5840,1471],{"class":262},[252,5842,5843],{"class":900},"deb https:\u002F\u002Fdeveloper.download.nvidia.com\u002Fcompute\u002Fcuda\u002Frepos\u002Fubuntu2004\u002Fx86_64\u002F \u002F",[252,5845,3062],{"class":262},[252,5847,5848,5850,5853],{"class":254,"line":354},[252,5849,5806],{"class":896},[252,5851,5852],{"class":900}," apt-get",[252,5854,5855],{"class":900}," update\n",[252,5857,5858,5860,5862,5865,5867],{"class":254,"line":364},[252,5859,5806],{"class":896},[252,5861,5852],{"class":900},[252,5863,5864],{"class":900}," -y",[252,5866,1175],{"class":900},[252,5868,5869],{"class":900}," cuda\n",[26,5871,5872,5873,126],{},"After everything is installed successfully it's time to install ",[58,5874,5762],{"href":5593,"rel":5875},[62],[242,5877,5879],{"className":886,"code":5878,"filename":888,"language":889,"meta":188,"style":188},"wget https:\u002F\u002Fhashcat.net\u002Ffiles\u002Fhashcat-6.1.1.7z\nsudo apt install p7zip\np7zip -d hashcat-6.1.1.7z\nsudo cp hashcat-6.1.1\u002Fhashcat64.bin \u002Fusr\u002Fbin\u002F\nsudo ln -s \u002Fusr\u002Fbin\u002Fhashcat64.bin \u002Fusr\u002Fbin\u002Fhashcat\nsudo cp -Rv hashcat-6.1.1\u002FOpenCL\u002F \u002Fusr\u002Fbin\u002F\nsudo cp hashcat-6.1.1\u002Fhashcat.hcstat2 \u002Fusr\u002Fbin\u002F\nsudo cp hashcat-6.1.1\u002Fhashcat.hctune \u002Fusr\u002Fbin\u002F\n",[249,5880,5881,5888,5900,5911,5924,5940,5954,5965],{"__ignoreMap":188},[252,5882,5883,5885],{"class":254,"line":255},[252,5884,5798],{"class":896},[252,5886,5887],{"class":900}," https:\u002F\u002Fhashcat.net\u002Ffiles\u002Fhashcat-6.1.1.7z\n",[252,5889,5890,5892,5895,5897],{"class":254,"line":189},[252,5891,5806],{"class":896},[252,5893,5894],{"class":900}," apt",[252,5896,1175],{"class":900},[252,5898,5899],{"class":900}," p7zip\n",[252,5901,5902,5905,5908],{"class":254,"line":326},[252,5903,5904],{"class":896},"p7zip",[252,5906,5907],{"class":900}," -d",[252,5909,5910],{"class":900}," hashcat-6.1.1.7z\n",[252,5912,5913,5915,5918,5921],{"class":254,"line":343},[252,5914,5806],{"class":896},[252,5916,5917],{"class":900}," cp",[252,5919,5920],{"class":900}," hashcat-6.1.1\u002Fhashcat64.bin",[252,5922,5923],{"class":900}," \u002Fusr\u002Fbin\u002F\n",[252,5925,5926,5928,5931,5934,5937],{"class":254,"line":354},[252,5927,5806],{"class":896},[252,5929,5930],{"class":900}," ln",[252,5932,5933],{"class":900}," -s",[252,5935,5936],{"class":900}," \u002Fusr\u002Fbin\u002Fhashcat64.bin",[252,5938,5939],{"class":900}," \u002Fusr\u002Fbin\u002Fhashcat\n",[252,5941,5942,5944,5946,5949,5952],{"class":254,"line":364},[252,5943,5806],{"class":896},[252,5945,5917],{"class":900},[252,5947,5948],{"class":900}," -Rv",[252,5950,5951],{"class":900}," hashcat-6.1.1\u002FOpenCL\u002F",[252,5953,5923],{"class":900},[252,5955,5956,5958,5960,5963],{"class":254,"line":381},[252,5957,5806],{"class":896},[252,5959,5917],{"class":900},[252,5961,5962],{"class":900}," hashcat-6.1.1\u002Fhashcat.hcstat2",[252,5964,5923],{"class":900},[252,5966,5967,5969,5971,5974],{"class":254,"line":398},[252,5968,5806],{"class":896},[252,5970,5917],{"class":900},[252,5972,5973],{"class":900}," hashcat-6.1.1\u002Fhashcat.hctune",[252,5975,5923],{"class":900},[26,5977,5978,5979,126],{},"And now let's test that Hashcat is installed properly and recognize our GPU by running ",[249,5980,5981],{},"sudo hashcat --benchmark",[26,5983,5984],{},[41,5985],{"alt":5986,"className":5987,"height":5988,"src":5989,"width":5990},"Hashcat recognize our GPU",[45,46],305,"\u002Fposts\u002F2020\u002Fcrack-the-hash\u002Fhashcat-benchmark.webp",632,[458,5992,5994],{"id":5993},"_4-lets-get-down-to-business","4. Let's get down to business",[26,5996,5997],{},"The first thing we need is a password list. You can make your own password (word) list, but there is no need to work that hard when there is so much stuff free on the internet just waiting for you. And it's not just free it's proven! There are cleartext passwords from leaked databases, so you know it's words\u002Fnumber combinations that are used in the real world by users.",[26,5999,6000,6001,6006,6007,6012],{},"The best place to get some of it is the ",[58,6002,6005],{"href":6003,"rel":6004},"https:\u002F\u002Fgithub.com\u002Fdanielmiessler\u002FSecLists\u002Ftree\u002Fmaster\u002FPasswords",[62],"SecList on GitHub",", and one of the most popular list is the ",[58,6008,6011],{"href":6009,"rel":6010},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FRockYou",[62],"rockyou"," one. So let's download it to our VM and use that list to crack some passwords.",[242,6014,6016],{"className":886,"code":6015,"filename":888,"language":889,"meta":188,"style":188},"wget https:\u002F\u002Fgithub.com\u002Fbrannondorsey\u002Fnaive-hashcat\u002Freleases\u002Fdownload\u002Fdata\u002Frockyou.txt\n",[249,6017,6018],{"__ignoreMap":188},[252,6019,6020,6022],{"class":254,"line":255},[252,6021,5798],{"class":896},[252,6023,6024],{"class":900}," https:\u002F\u002Fgithub.com\u002Fbrannondorsey\u002Fnaive-hashcat\u002Freleases\u002Fdownload\u002Fdata\u002Frockyou.txt\n",[26,6026,6027],{},"To crack a password you first need to know the hash type of that password - which hashing algorithm they use to encrypt the password (there are multiple ways to discover it, and also a lot of projects that are trying to do that automatically, but this is not the purpose of this post, so Google it).",[26,6029,6030,6031,6036],{},"For testing I encrypt some text with ",[58,6032,6035],{"href":6033,"rel":6034},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FBcrypt",[62],"Bcrypt hash function",", here are the results:",[242,6038,6042],{"className":6039,"code":6040,"language":6041,"meta":188,"style":188},"language-txt shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","$2b$04$jysbNjr164hK4E7tqk2B.OJoj4qAg9xHF4gLqain2m9pGb1bcsRf6\n$2b$04$T.E6VXlAlt\u002FK\u002FgfVBBrs8eULxwBTDGVZ1CNsE.yJX5Yt9LjqH5bDO\n$2b$04$XMOmhaV4EM.8fDCVEk3GYOmSbHvVoSx1cDyxhSeebBmCZyGisq7sG\n$2b$04$GIVH57JcU5wZyfwyu0A6Uuq.WrUDFLQfPC5HGHDsdfPfdWvP2jklS\n","txt",[249,6043,6044,6049,6054,6059],{"__ignoreMap":188},[252,6045,6046],{"class":254,"line":255},[252,6047,6048],{},"$2b$04$jysbNjr164hK4E7tqk2B.OJoj4qAg9xHF4gLqain2m9pGb1bcsRf6\n",[252,6050,6051],{"class":254,"line":189},[252,6052,6053],{},"$2b$04$T.E6VXlAlt\u002FK\u002FgfVBBrs8eULxwBTDGVZ1CNsE.yJX5Yt9LjqH5bDO\n",[252,6055,6056],{"class":254,"line":326},[252,6057,6058],{},"$2b$04$XMOmhaV4EM.8fDCVEk3GYOmSbHvVoSx1cDyxhSeebBmCZyGisq7sG\n",[252,6060,6061],{"class":254,"line":343},[252,6062,6063],{},"$2b$04$GIVH57JcU5wZyfwyu0A6Uuq.WrUDFLQfPC5HGHDsdfPfdWvP2jklS\n",[26,6065,6066,6067,6072,6073,6076,6077,983],{},"Now, go to the ",[58,6068,6071],{"href":6069,"rel":6070},"https:\u002F\u002Fhashcat.net\u002Fwiki\u002Fdoku.php?id=example_hashes",[62],"documentation of Hashcat"," and check what ",[249,6074,6075],{},"Hash-Mode"," number is the hash algorithm that I used (in our case it's ",[249,6078,6079],{},"3200",[26,6081,6082,6083,6086,6087,6090,6091,6094,6095,6098],{},"A basic running is just to use the ",[249,6084,6085],{},"m"," flag with the mode 3200, the ",[249,6088,6089],{},"o"," flag to point Hashcat where to put all of the cracked passwords, and then just the file with the hashes (I put them in ",[249,6092,6093],{},"test.txt"," on the VM) and the file with the cleartext passwords list (the ",[249,6096,6097],{},"rockyou.txt"," we downloaded earlier).",[242,6100,6102],{"className":886,"code":6101,"filename":888,"language":889,"meta":188,"style":188},"sudo hashcat -m 3200 -o cracked.txt test.txt rockyou.txt\n",[249,6103,6104],{"__ignoreMap":188},[252,6105,6106,6108,6111,6114,6117,6120,6123,6126],{"class":254,"line":255},[252,6107,5806],{"class":896},[252,6109,6110],{"class":900}," hashcat",[252,6112,6113],{"class":900}," -m",[252,6115,6116],{"class":1782}," 3200",[252,6118,6119],{"class":900}," -o",[252,6121,6122],{"class":900}," cracked.txt",[252,6124,6125],{"class":900}," test.txt",[252,6127,6128],{"class":900}," rockyou.txt\n",[26,6130,6131],{},[41,6132],{"alt":6133,"className":6134,"height":6135,"src":6136,"width":6137},"Basic Hash Cracking",[45,46],922,"\u002Fposts\u002F2020\u002Fcrack-the-hash\u002Fbasic-hash-cracking.webp",678,[26,6139,6140,6141,6144,6145,983],{},"While Hashcat is running you can write ",[249,6142,6143],{},"s"," on the terminal to check the status of the process. You'll see how much time it estimates the run will take, how many hashes it's already cracked, and what the progress is (how many passwords it already checked from how many there are in the file we choose - ",[249,6146,6097],{},[26,6148,6149,6150,6152,6153,6156,6157,6160,6161,6164,6165,6167,6168,6171],{},"It'll take a while to go over all the passwords in ",[249,6151,6097],{},", because it won't be able to crack all four hashes I gave you. When it'll finish you can see in the ",[249,6154,6155],{},"cracked.txt"," file that Hashcat only cracked 3 out of 4 hashes. The fourth one is ",[249,6158,6159],{},"$2b$04$GIVH57JcU5wZyfwyu0A6Uuq.WrUDFLQfPC5HGHDsdfPfdWvP2jklS"," which is the Bcrypt of ",[249,6162,6163],{},"aSd12345",", this text is present in the ",[249,6166,6097],{}," file but as all lowercase letters (",[249,6169,6170],{},"asd12345",", which is also the third hash). This is where the rules of Hashcat come into play.",[26,6173,6174],{},[41,6175],{"alt":6176,"className":6177,"height":6178,"src":6179,"width":6180},"Basic Cracking Results",[45,46],331,"\u002Fposts\u002F2020\u002Fcrack-the-hash\u002Fbasic-cracking-results.webp",614,[26,6182,6183,6184,6189],{},"Using a ruleset we can change our wordlist to contain more variations of the existing text. And again, we don't need to invent the wheel here, there are a lot of rules that people already write for Hashcat and one of them is ",[58,6185,6188],{"href":6186,"rel":6187},"https:\u002F\u002Fgithub.com\u002Fpraetorian-inc\u002FHob0Rules",[62],"Hob0rules"," which will help us do all the variations of lowercase and uppercase letters in our wordlist.",[26,6191,6192,6193,6196],{},"So let's use those rules, first download the repo, next we need to ",[249,6194,6195],{},"unzip"," the zip file, and finally change the directory name.",[242,6198,6200],{"className":886,"code":6199,"filename":888,"language":889,"meta":188,"style":188},"wget https:\u002F\u002Fgithub.com\u002Fpraetorian-inc\u002FHob0Rules\u002Farchive\u002Fmaster.zip\nunzip master.zip\nmv Hob0Rules-master\u002F Hob0Rules\n",[249,6201,6202,6209,6216],{"__ignoreMap":188},[252,6203,6204,6206],{"class":254,"line":255},[252,6205,5798],{"class":896},[252,6207,6208],{"class":900}," https:\u002F\u002Fgithub.com\u002Fpraetorian-inc\u002FHob0Rules\u002Farchive\u002Fmaster.zip\n",[252,6210,6211,6213],{"class":254,"line":189},[252,6212,6195],{"class":896},[252,6214,6215],{"class":900}," master.zip\n",[252,6217,6218,6221,6224],{"class":254,"line":326},[252,6219,6220],{"class":896},"mv",[252,6222,6223],{"class":900}," Hob0Rules-master\u002F",[252,6225,6226],{"class":900}," Hob0Rules\n",[26,6228,6229,6230,6233],{},"To run Hashcat again, but now with the rules we downloaded all we need to do is to use the ",[249,6231,6232],{},"r"," flag and point it to the path of the rule file.",[242,6235,6237],{"className":886,"code":6236,"filename":888,"language":889,"meta":188,"style":188},"sudo hashcat -m 3200 -o cracked.txt -r Hob0Rules\u002Fhob064.rule test.txt rockyou.txt\n",[249,6238,6239],{"__ignoreMap":188},[252,6240,6241,6243,6245,6247,6249,6251,6253,6256,6259,6261],{"class":254,"line":255},[252,6242,5806],{"class":896},[252,6244,6110],{"class":900},[252,6246,6113],{"class":900},[252,6248,6116],{"class":1782},[252,6250,6119],{"class":900},[252,6252,6122],{"class":900},[252,6254,6255],{"class":900}," -r",[252,6257,6258],{"class":900}," Hob0Rules\u002Fhob064.rule",[252,6260,6125],{"class":900},[252,6262,6128],{"class":900},[26,6264,6265,6266,6268,6269,6271,6272,6274],{},"Now if you write ",[249,6267,6143],{}," in the terminal to check the status progress of the cracking process you can see that instead of 57,377,536 passwords combinations that the ",[249,6270,6097],{}," file contains, Hashcat checks 3,672,162,304 passwords combinations, because it uses uppercase and lowercase combinations of the same text that the ",[249,6273,6097],{}," file contains.",[458,6276,968],{"id":967},[26,6278,6279],{},"That's it, we cracked the hash! And we leverage the cloud to do it faster and cheaper. If you want to continue, you can rent a couple of GPUs in Google (in the same VM) and leverage them to crack your hashes much faster! Also, you can read the Hashcat documentation and try to use other methods to speed up your cracking.",[772,6281,6282],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":188,"searchDepth":189,"depth":189,"links":6284},[6285,6286,6287,6288,6289],{"id":5607,"depth":189,"text":5608},{"id":5665,"depth":189,"text":5666},{"id":5747,"depth":189,"text":5748},{"id":5993,"depth":189,"text":5994},{"id":967,"depth":189,"text":968},"2020-11-01","Cracking password hashes with Hashcat on a GPU-equipped GCP instance.",{"src":6293},"\u002Fposts\u002F2020\u002Fcrack-the-hash\u002Fcrack-passwords.webp",{},"\u002Fblog\u002F2020\u002Fcrack-the-hash",{"title":5576,"description":6291},"blog\u002F2020\u002Fcrack-the-hash","Kxdt51ps9kzVonCgq3UMNn_MNYkQUIcIE-pk-uYZuS8",{"id":6300,"title":6301,"author":6302,"badge":6304,"body":6305,"date":7264,"description":7265,"draft":193,"extension":194,"image":7266,"meta":7268,"navigation":198,"path":7269,"seo":7270,"stem":7271,"__hash__":7272},"posts\u002Fblog\u002F2020\u002Flike-a-spy-with-hak5-toys.md","Like A Spy With Hak5 Toys",{"name":16,"to":17,"avatar":6303},{"src":19},{"label":5580},{"type":23,"value":6306,"toc":7257},[6307,6321,6336,6340,6343,6349,6371,6374,6381,6403,6412,6423,6436,6445,6460,6485,6492,6503,6513,6536,6545,6555,6571,6589,6629,6640,6649,6656,6665,6669,6674,6677,6687,6694,6707,6722,6731,6742,6750,6753,6768,6771,6786,6792,6795,6798,6805,6816,6823,6841,6852,6856,6861,6864,6867,6876,6893,6906,6924,6927,6934,6947,6954,6971,6975,6983,6990,7021,7038,7059,7085,7097,7119,7125,7170,7176,7211,7221,7237,7246,7248,7251,7254],[26,6308,6309,6310,6315,6316,6320],{},"One of the things you always see in spy movies is how the main character plants a covert device to monitor the user's computer (screen and keystrokes). As a kid I thought \"This is very cool!\", and I always wanted to be able to do this. ",[58,6311,6314],{"href":6312,"rel":6313},"https:\u002F\u002Fhak5.org",[62],"Hak5"," makes it very easy to accomplish with a combination of couple of their ",[6317,6318,6319],"del",{},"toys"," tools.",[26,6322,6323,6324,6329,6330,6335],{},"What we'll do is to connect 2 devices, the ",[58,6325,6328],{"href":6326,"rel":6327},"https:\u002F\u002Fshop.hak5.org\u002Fproducts\u002Fscreen-crab",[62],"screen crab"," to see the user's screen (video stream, in real time), and a ",[58,6331,6334],{"href":6332,"rel":6333},"https:\u002F\u002Fshop.hak5.org\u002Fproducts\u002Fkey-croc",[62],"key croc"," to key log the user's keystrokes.",[458,6337,6339],{"id":6338},"_1-c2-cloud","1. C2 Cloud",[26,6341,6342],{},"Before we even start to mess around with the physical toys, let's create a C2 Cloud instance on the cloud. You can do it on your local machine, but let's pretend this is a real operation and in a real environment we don't want the target to connect directly to our machine and we don't want to expose our local machine to the internet.",[6344,6345,6346],"blockquote",{},[26,6347,6348],{},"Cloud C2 is a self-hosted web-based command and control suite for networked Hak5 gear that lets you pentest from anywhere.",[26,6350,6351,6352,6357,6358,6361,6362,5644,6367,6370],{},"I chose to do it in ",[58,6353,6356],{"href":6354,"rel":6355},"https:\u002F\u002Fdigitalocean.com",[62],"digitalocean.com"," because they're easy, cheap and I love their community and open source support. So, their VPS called ",[249,6359,6360],{},"droplets",", after you registered and confirmed your email, you'll see it at the top of the left side menu. When you're at the ",[58,6363,6366],{"href":6364,"rel":6365},"https:\u002F\u002Fcloud.digitalocean.com\u002Fdroplets",[62],"droplets page",[951,6368,6369],{},"\"Create Droplet\""," button right at the center of the page (if you don't have any, if you already have some, you'll see it as a green button at the top of the page).",[26,6372,6373],{},"We'll choose an Ubuntu distribution, a basic plan (shared cpu, the $5\u002Fmonth should do the job), and add a volume (I chose to add 20GB, I think it'll be enough for all the loot). Now you can choose whichever datacenter region you want, and don't forget to add your SSH key.",[26,6375,6376],{},[41,6377],{"alt":6378,"className":6379,"src":6380},"My digital ocean droplet configuration",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fdigital-ocean-create-droplet.webp",[26,6382,6383,6384,6387,6388,6393,6394,6397,6398,6402],{},"We have two ways to create the C2 instance, use the instance public IP (your droplet will receive one right after it will start) as a ",[249,6385,6386],{},"hostname"," or use a DNS name. I'll use the DNS name way, so we can add https support with ",[58,6389,6392],{"href":6390,"rel":6391},"https:\u002F\u002Fletsencrypt.org",[62],"Let's Encrypt",". If you choose to do this the way I do it, you'll need a domain. I'll use ",[249,6395,6396],{},"cloud-c2.dev",", and I'll create a custom DNS A record, so ",[58,6399,6400],{"href":6400,"rel":6401},"https:\u002F\u002Fcloud-c2.dev",[62]," will point to the instance public IP.",[26,6404,6405],{},[41,6406],{"alt":6407,"className":6408,"src":6409,"height":6410,"width":6411},"Google Domains custom record",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fgoogle-domains-custom-record.webp",337,1034,[26,6413,6414,6415,6418,6419,6422],{},"You'll need to wait a little (probably couple of hours) to let the DNS propagation to finish, plus ",[58,6416,6392],{"href":6390,"rel":6417},[62]," DNS seems to take a little bit longer than the Google ones (keep it in mind in case you encounter ",[249,6420,6421],{},"TLS handshake error from **** acme\u002Fautocert: missing certificate"," error).",[26,6424,6425,6426,6431,6432,6435],{},"To download the ",[58,6427,6430],{"href":6428,"rel":6429},"https:\u002F\u002Fshop.hak5.org\u002Fproducts\u002Fc2#c2-versions",[62],"c2 community edition from hak5"," you need to choose ",[951,6433,6434],{},"\"FREE DOWNLOAD\""," and then to checkout, at the end of the checkout process you'll get an email with the download link and a license key, save it somewhere safe.",[26,6437,6438],{},[41,6439],{"alt":6440,"className":6441,"src":6442,"height":6443,"width":6444},"Hak5 C2 Email",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fc2-email.webp",552,944,[26,6446,6447,6448,6451,6452,6455,6456,6459],{},"Let's finish our droplet setup by opening the relevant ports. Click on the ",[951,6449,6450],{},"\"Networking\""," tab at the left menu, and from there go to the ",[951,6453,6454],{},"\"Firewalls\""," page. Click on ",[951,6457,6458],{},"\"Create Firewall\""," button, give it a name and add a couple of inbound rules:",[151,6461,6462,6469,6475],{},[154,6463,6464,6465,6468],{},"Port ",[249,6466,6467],{},"2022"," so the Hak5 gear will be able to communicate with our C2.",[154,6470,6464,6471,6474],{},[249,6472,6473],{},"443"," so we can open our C2 dashboard in the browser with SSL\u002FTLS.",[154,6476,6464,6477,6480,6481,6484],{},[249,6478,6479],{},"80"," so ",[58,6482,6392],{"href":6390,"rel":6483},[62]," can validate the domain and put back the certificate.",[26,6486,6487],{},[41,6488],{"alt":6489,"className":6490,"src":6491},"My Digital Ocean firewall configuration",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fdigital-ocean-firewall.webp",[26,6493,6494,6495,6498,6499,6502],{},"Don't forget to add our droplet in ",[951,6496,6497],{},"\"Apply to Droplets\""," input, right before the green ",[951,6500,6501],{},"\"Create firewall\""," button at the bottom.",[26,6504,6505,6506,6509,6510,983],{},"Now the fun part begins, let's ssh to our server (replace ",[249,6507,6508],{},"\u003CDROPLET-IP>"," with your droplet ip, in my case it's ",[249,6511,6512],{},"165.227.156.17",[242,6514,6516],{"className":886,"code":6515,"filename":888,"language":889,"meta":188,"style":188},"ssh root@\u003CDROPLET-IP>\n",[249,6517,6518],{"__ignoreMap":188},[252,6519,6520,6523,6526,6528,6531,6534],{"class":254,"line":255},[252,6521,6522],{"class":896},"ssh",[252,6524,6525],{"class":900}," root@",[252,6527,1914],{"class":262},[252,6529,6530],{"class":900},"DROPLET-I",[252,6532,6533],{"class":266},"P",[252,6535,1935],{"class":262},[26,6537,6538],{},[41,6539],{"alt":6540,"className":6541,"src":6542,"height":6543,"width":6544},"SSH into droplet",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fssh-to-droplet.webp",475,573,[26,6546,6547,6548,6550,6551,6554],{},"And install the ",[249,6549,6195],{}," package so we can unzip the ",[249,6552,6553],{},"zip"," file we'll download with all the edition for Windows, Mac and Linux of C2 from Hak5.",[242,6556,6558],{"className":886,"code":6557,"filename":888,"language":889,"meta":188,"style":188},"sudo apt install unzip\n",[249,6559,6560],{"__ignoreMap":188},[252,6561,6562,6564,6566,6568],{"class":254,"line":255},[252,6563,5806],{"class":896},[252,6565,5894],{"class":900},[252,6567,1175],{"class":900},[252,6569,6570],{"class":900}," unzip\n",[26,6572,6573,6574,6577,6578,6580,6581,6584,6585,6588],{},"Now let's build a command to download the c2 (you can get the download URL from the email), save the file in the name ",[249,6575,6576],{},"c2.zip",", unzip it, and run the relevant binary for our OS with the ",[249,6579,6386],{}," point to our domain, and the ",[249,6582,6583],{},"https"," flag so ",[58,6586,6392],{"href":6390,"rel":6587},[62]," will automatically create a certificate for us.",[242,6590,6592],{"className":886,"code":6591,"filename":888,"language":889,"meta":188,"style":188},"wget https:\u002F\u002Fc2.hak5.org\u002Fdownload\u002Fcommunity -O c2.zip && unzip c2.zip && .\u002Fc2_community-linux-64 -hostname cloud-c2.dev -https\n",[249,6593,6594],{"__ignoreMap":188},[252,6595,6596,6598,6601,6604,6607,6610,6613,6615,6617,6620,6623,6626],{"class":254,"line":255},[252,6597,5798],{"class":896},[252,6599,6600],{"class":900}," https:\u002F\u002Fc2.hak5.org\u002Fdownload\u002Fcommunity",[252,6602,6603],{"class":900}," -O",[252,6605,6606],{"class":900}," c2.zip",[252,6608,6609],{"class":262}," &&",[252,6611,6612],{"class":896}," unzip",[252,6614,6606],{"class":900},[252,6616,6609],{"class":262},[252,6618,6619],{"class":896}," .\u002Fc2_community-linux-64",[252,6621,6622],{"class":900}," -hostname",[252,6624,6625],{"class":900}," cloud-c2.dev",[252,6627,6628],{"class":900}," -https\n",[26,6630,6631,6632,6635,6636,6639],{},"The first time C2 runs, a database file is generated (named ",[249,6633,6634],{},"c2.db"," by default) in the same directory as the C2 binary. In that first run you'll see it print a ",[951,6637,6638],{},"\"Setup token\"",", copy it, we'll need it to continue the setup process.",[26,6641,6642],{},[41,6643],{"alt":6644,"className":6645,"src":6646,"height":6647,"width":6648},"First C2 Run",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fc2-is-running.webp",245,733,[26,6650,6651,6652,6655],{},"Now, when we head over to ",[58,6653,6400],{"href":6400,"rel":6654},[62]," we can see the initial setup of C2. Now you'll need that setup token that the C2 generated in the first run, and also the license key.",[26,6657,6658],{},[41,6659],{"alt":6660,"className":6661,"src":6662,"height":6663,"width":6664},"Cloud C2 initial setup",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fcloud-c2-initial-setup.webp",507,566,[458,6666,6668],{"id":6667},"_2-screen-crab","2. Screen Crab",[6344,6670,6671],{},[26,6672,6673],{},"A stealthy video man-in-the-middle that captures screenshots or videos to disk and streams live to the Internet for remote viewing.",[26,6675,6676],{},"The Screen Crab, by default, saves photos every couple of seconds to the SD card that you plug in the back of the device. But to get the most out of this device you need to connect it to the Cloud C2 dashboard, then you'll be able to remotely view, configure, and manage the device.",[26,6678,6679,6680,6683,6684,126],{},"It's quite simple to do this, let's go to the ",[249,6681,6682],{},"devices"," tab, and then (if you don't have any device enrolled yet), you'll see a big blue button says ",[951,6685,6686],{},"\"Add Device\"",[26,6688,6689],{},[41,6690],{"alt":6691,"className":6692,"src":6693},"Add a new device",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fc2-add-device.webp",[26,6695,6696,6697,6700,6701,6703,6704,6706],{},"Then, call the device whatever you want, and choose the type, in our case right now it's ",[951,6698,6699],{},"\"Screen Crab\"",", and click ",[951,6702,6686],{},". After you did it, you'll see it in the list at the same ",[249,6705,6682],{}," page, click on it to configure the device.",[26,6708,6709,6710,6713,6714,6717,6718,6721],{},"At the left side a menu will be opened, click on the ",[951,6711,6712],{},"\"Setup\""," button and then ",[951,6715,6716],{},"\"Download\"",", a file named ",[249,6719,6720],{},"device.config"," will start to be downloaded, copy this file to the root of the Screen Crab SD card.",[26,6723,6724],{},[41,6725],{"alt":6726,"className":6727,"src":6728,"height":6729,"width":6730},"Screen Crab setup",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fscreen-carb-setup.webp",454,1016,[26,6732,6733,6734,6737,6738,6741],{},"Also, create a file named ",[249,6735,6736],{},"config.txt"," and in it add the wifi configuration. The first parameter is the network name (SSID), and the second one is the network password. Note that if your network or password contains spaces or special characters you'll need to add a back slash (",[249,6739,6740],{},"\\",") at the start of each and every one of them.",[866,6743,6744,6747],{},[154,6745,6746],{},"WIFI_SSID – the network name",[154,6748,6749],{},"WIFI_PASS – the WPA-PSK password",[26,6751,6752],{},"For example:",[242,6754,6756],{"className":6039,"code":6755,"filename":6736,"language":6041,"meta":188,"style":188},"WIFI_SSID This network\nWIFI_PASS The P@$$word!!\n",[249,6757,6758,6763],{"__ignoreMap":188},[252,6759,6760],{"class":254,"line":255},[252,6761,6762],{},"WIFI_SSID This network\n",[252,6764,6765],{"class":254,"line":189},[252,6766,6767],{},"WIFI_PASS The P@$$word!!\n",[26,6769,6770],{},"Will be:",[242,6772,6774],{"className":6039,"code":6773,"filename":6736,"language":6041,"meta":188,"style":188},"WIFI_SSID This\\ network\nWIFI_PASS The\\ P\\@\\$\\$word\\!\\!\n",[249,6775,6776,6781],{"__ignoreMap":188},[252,6777,6778],{"class":254,"line":255},[252,6779,6780],{},"WIFI_SSID This\\ network\n",[252,6782,6783],{"class":254,"line":189},[252,6784,6785],{},"WIFI_PASS The\\ P\\@\\$\\$word\\!\\!\n",[26,6787,6788,6789,6791],{},"After you created this file, add it to the root of the SD card that goes into the Screen Crab (together with ",[249,6790,6720],{},"). In my case I just add the configuration of my home wifi, but in a real environment I would add a simple old android device in the bathroom floor and create an AP from it, or a simple raspberry pi with an expansion card, or even crack the wifi password of that office in advance.",[26,6793,6794],{},"Now we're ready to connect our device to any HDMI (computer screen, projector, a conference room tv, chromecast, etc). We need to connect the input HDMI to the port in the antenna (and the button) side, and the output (to the screen\u002Ftv\u002Fprojector) to the port on the other side (where the usb-c port located). And finally the usb-c port to a power source.",[26,6796,6797],{},"After all is connected, we'll see a green light from the LED of the device for about 30 seconds. Then, a cyan color when it's connecting to the wifi, and finally a solid blue color when it has input from the HDMI port. And a couple of seconds after that we can see our Screen Crab is online and connected to our C2.",[26,6799,6800],{},[41,6801],{"alt":6802,"className":6803,"src":6804},"Screen Crab is online",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fscreen-crab-is-online.webp",[26,6806,6807,6808,6811,6812,6815],{},"When we toggle the ",[249,6809,6810],{},"streaming"," button we'll get the screenshots live and can see everything right as it happens. And if we want to download one of the images to our local machine we have all of them in the ",[951,6813,6814],{},"\"Loot\""," tab in the left menu.",[26,6817,6818],{},[41,6819],{"alt":6820,"className":6821,"src":6822},"Screen Crab configuration",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fscreen-crab-configuration.webp",[26,6824,6825,6829,6830,6833,6834,6836,6837,6840],{},[6826,6827],"binding",{"value":6828},"\u003C admonition type=bug title=\"Debugging\" open=true >","\nIf you encounter some problems, you can add ",[249,6831,6832],{},"DEBUG_LOG ON"," as a third line in the ",[249,6835,6736],{}," file and the Screen Crab will save the logs to a ",[249,6838,6839],{},"crab.log"," file so you can see what's going on.",[26,6842,6843,6844,4754,6846,6848,6849],{},"Another way is to just reset everything by delete all of the files the Screen Crab will create on the SD card (except from the ",[249,6845,6736],{},[249,6847,6720],{}," that we add earlier).\n",[6826,6850],{"value":6851},"\u003C \u002Fadmonition >",[458,6853,6855],{"id":6854},"_3-key-croc","3. Key Croc",[6344,6857,6858],{},[26,6859,6860],{},"A keylogger armed with pentest tools, remote access and payloads that trigger multi-vector attacks when chosen keywords are typed.",[26,6862,6863],{},"The Key Croc is the second part of our spy tool, because we don't want to get just the screen, we want the keystrokes too. And with the Key Croc we can even trigger stuff with it and inject keystrokes to the target computer, but this is out of scope for this post.",[26,6865,6866],{},"Plug your Key Croc to the computer and click on the hidden key at the back of the device with a sim tool or a paper clip, then the light of the LED will turn off and then turn on with a blue color. This means the device is in arming mode - the device will emulate both a serial device and USB flash disk, so it's easy to just see a new look inside the drive.",[26,6868,6869],{},[41,6870],{"alt":6871,"className":6872,"src":6873,"height":6874,"width":6875},"The Key Croc drive",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fkey-croc-drive.webp",395,399,[26,6877,6878,6879,6882,6883,6886,6887,6889,6890,6892],{},"Let's connect our Key Croc to our C2. Go to the C2 dashboard and add a new device in ",[951,6880,6881],{},"\"Devices\""," page, choose the ",[951,6884,6885],{},"\"Key Croc\""," as the device type, and then enter to the device configuration page. From there just hit the ",[951,6888,6712],{}," button at the left menu and a ",[249,6891,6720],{}," file will be downloaded, as with the Screen Crab, this file should go to the root directory in the Key Croc.",[26,6894,6895,6896,6898,6899,120,6902,6905],{},"And let's add our wifi configuration so it would be able to connect to our C2 server. Like with the Screen Crab we need to edit the ",[249,6897,6736],{}," file (we already have the file in this case, and there're quite a few comments in there), we'll uncomment the ",[249,6900,6901],{},"WIFI_SSID",[249,6903,6904],{},"WIFI_PASS"," lines and add our wifi configuration there, in the same exact way we did with the Screen Crab, but in this case we have a new option to play with, an ssh one, so we can connect to the Key Croc via ssh and program it on the fly!",[242,6907,6909],{"className":6039,"code":6908,"filename":6736,"language":6041,"meta":188,"style":188},"WIFI_SSID This\\ network\nWIFI_PASS The\\ P\\@\\$\\$word\\!\\!\nSSH ENABLE\n",[249,6910,6911,6915,6919],{"__ignoreMap":188},[252,6912,6913],{"class":254,"line":255},[252,6914,6780],{},[252,6916,6917],{"class":254,"line":189},[252,6918,6785],{},[252,6920,6921],{"class":254,"line":326},[252,6922,6923],{},"SSH ENABLE\n",[26,6925,6926],{},"We're ready to connect the device to a test keyboard. When we plug the device in, the LED is white, and once we connect the keyboard dongle to the usb of the Key Croc, the LED is turned off - which says that we're in business. Couple of minutes later and we have a connection to our C2.",[26,6928,6929],{},[41,6930],{"alt":6931,"className":6932,"src":6933},"Key Croc is connected to our C2",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fc2-key-croc-online.webp",[26,6935,6936,6937,6940,6941,6946],{},"In the Key Croc configuration we can see live keystrokes (and the history), enable ",[249,6938,6939],{},"QUACK"," mode - which enables us to inject keystrokes using the Hak5 Ducky Script, view the payloads on the device and create new ones (Hak5 have a ",[58,6942,6945],{"href":6943,"rel":6944},"https:\u002F\u002Fgithub.com\u002Fhak5\u002Fkeycroc-payloads",[62],"repo with all the open source payloads"," people created for the Key Croc with the Ducky Script), and even open an ssh to the device.",[26,6948,6949],{},[41,6950],{"alt":6951,"className":6952,"src":6953},"Key Croc configuration",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fkey-croc-configuration.webp",[26,6955,6956,6957,6960,6961,6964,6965,6967,6968,6970],{},"The Key Croc can literally be programmed to ",[249,6958,6959],{},"match"," keystrokes so every time the user types something we save the next ",[249,6962,6963],{},"n"," number of keystrokes or even the last ",[249,6966,6963],{}," number of keystrokes (before the user typed that ",[249,6969,6959],{}," string\u002Fregex). This is so powerful because now we don't have to filter from so much garbage like we usually need to with keyloggers.",[458,6972,6974],{"id":6973},"_4-cleanup","4. Cleanup",[26,6976,6977,6978,126],{},"Everything is connected and working, but as soon as we leave our Digital Ocean server, our C2 will stop. We need a way to keep it going without connecting to the server and run it manually (it also will stop if we get an error or something similar) - and the right way to accomplish it is with ",[58,6979,6982],{"href":6980,"rel":6981},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSystemd",[62],"systemd",[6344,6984,6985],{},[26,6986,6987,6989],{},[249,6988,6982],{}," is a suite of basic building blocks for a Linux system. It provides a system and service manager that runs as PID 1 and starts the rest of the system.",[26,6991,6992,6993,6996,6997,7000,7001,7004,7005,7008,7009,7012,7013,7016,7017,7020],{},"First we're going to move the c2 file to ",[249,6994,6995],{},"\u002Fusr\u002Flocal\u002Fbin",". Why this directory? Because this is the directory in Linux for programs that a normal user may run. ",[951,6998,6999],{},"\"usr\""," - ",[871,7002,7003],{},"U","NIX ",[871,7006,7007],{},"S","ystem ",[871,7010,7011],{},"R","esources, the location that system programs and libraries are stored. ",[951,7014,7015],{},"\"local\""," - for resources that were not shipped with the standard distribution. ",[951,7018,7019],{},"\"bin\""," - binary compiled executables.",[242,7022,7024],{"className":886,"code":7023,"filename":888,"language":889,"meta":188,"style":188},"sudo mv c2_community-linux-64 \u002Fusr\u002Flocal\u002Fbin\n",[249,7025,7026],{"__ignoreMap":188},[252,7027,7028,7030,7032,7035],{"class":254,"line":255},[252,7029,5806],{"class":896},[252,7031,5809],{"class":900},[252,7033,7034],{"class":900}," c2_community-linux-64",[252,7036,7037],{"class":900}," \u002Fusr\u002Flocal\u002Fbin\n",[26,7039,7040,7041,7044,7045,7047,7048,7050,7051,7054,7055,7058],{},"and then create a new directory (in ",[249,7042,7043],{},"\u002Fvar",") for our database (the ",[249,7046,6634],{}," file that was automatically created). Why in ",[249,7049,7043],{},"? Because according to Linux its abbreviation is ",[951,7052,7053],{},"\"variable\"",", and it should contain things that are prone to changes (such as websites, temporary files (",[249,7056,7057],{},"\u002Fvar\u002Ftmp",") and databases).",[242,7060,7062],{"className":886,"code":7061,"filename":888,"language":889,"meta":188,"style":188},"sudo mkdir \u002Fvar\u002Fc2\nsudo mv c2.db \u002Fvar\u002Fc2\n",[249,7063,7064,7074],{"__ignoreMap":188},[252,7065,7066,7068,7071],{"class":254,"line":255},[252,7067,5806],{"class":896},[252,7069,7070],{"class":900}," mkdir",[252,7072,7073],{"class":900}," \u002Fvar\u002Fc2\n",[252,7075,7076,7078,7080,7083],{"class":254,"line":189},[252,7077,5806],{"class":896},[252,7079,5809],{"class":900},[252,7081,7082],{"class":900}," c2.db",[252,7084,7073],{"class":900},[26,7086,7087,7088,7090,7091,7093,7094,7096],{},"Now we need to create a new ",[249,7089,6982],{}," service. This is the \"right way\" to keep the process alive once we leave the ssh connection, because this way (with ",[249,7092,6982],{},") if it crashes the ",[249,7095,6982],{}," will automatically reboot it and also we can capture the process logs to check what happened.",[242,7098,7100],{"className":886,"code":7099,"filename":888,"language":889,"meta":188,"style":188},"sudo touch \u002Fetc\u002Fsystemd\u002Fsystem\u002Fc2.service\nnano \u002Fetc\u002Fsystemd\u002Fsystem\u002Fc2.service\n",[249,7101,7102,7112],{"__ignoreMap":188},[252,7103,7104,7106,7109],{"class":254,"line":255},[252,7105,5806],{"class":896},[252,7107,7108],{"class":900}," touch",[252,7110,7111],{"class":900}," \u002Fetc\u002Fsystemd\u002Fsystem\u002Fc2.service\n",[252,7113,7114,7117],{"class":254,"line":189},[252,7115,7116],{"class":896},"nano",[252,7118,7111],{"class":900},[26,7120,7121,7122,7124],{},"And paste the text below (change the ",[249,7123,6396],{}," to your domain name), save and exit nano.",[242,7126,7128],{"className":6039,"code":7127,"language":6041,"meta":188,"style":188},"[Unit]\nDescription=Cloud C2\nAfter=c2.service\n[Service]\nType=idle\nExecStart=\u002Fusr\u002Flocal\u002Fbin\u002Fc2_community-linux-64 -hostname cloud-c2.dev -https -db \u002Fvar\u002Fc2\u002Fc2.db\n[Install]\nWantedBy=multi-user.target\n",[249,7129,7130,7135,7140,7145,7150,7155,7160,7165],{"__ignoreMap":188},[252,7131,7132],{"class":254,"line":255},[252,7133,7134],{},"[Unit]\n",[252,7136,7137],{"class":254,"line":189},[252,7138,7139],{},"Description=Cloud C2\n",[252,7141,7142],{"class":254,"line":326},[252,7143,7144],{},"After=c2.service\n",[252,7146,7147],{"class":254,"line":343},[252,7148,7149],{},"[Service]\n",[252,7151,7152],{"class":254,"line":354},[252,7153,7154],{},"Type=idle\n",[252,7156,7157],{"class":254,"line":364},[252,7158,7159],{},"ExecStart=\u002Fusr\u002Flocal\u002Fbin\u002Fc2_community-linux-64 -hostname cloud-c2.dev -https -db \u002Fvar\u002Fc2\u002Fc2.db\n",[252,7161,7162],{"class":254,"line":381},[252,7163,7164],{},"[Install]\n",[252,7166,7167],{"class":254,"line":398},[252,7168,7169],{},"WantedBy=multi-user.target\n",[26,7171,7172,7173,7175],{},"All we have left to do is to reload the ",[249,7174,6982],{}," daemon, enable the new service we just created (if it's not enabled the service will start and stop only when you write the commands to start and stop it, if it's enabled it'll automatically start when the server is back up), and finally start it.",[242,7177,7179],{"className":886,"code":7178,"filename":888,"language":889,"meta":188,"style":188},"sudo systemctl daemon-reload && systemctl enable c2.service && systemctl start c2.service\n",[249,7180,7181],{"__ignoreMap":188},[252,7182,7183,7185,7188,7191,7193,7195,7198,7201,7203,7205,7208],{"class":254,"line":255},[252,7184,5806],{"class":896},[252,7186,7187],{"class":900}," systemctl",[252,7189,7190],{"class":900}," daemon-reload",[252,7192,6609],{"class":262},[252,7194,7187],{"class":896},[252,7196,7197],{"class":900}," enable",[252,7199,7200],{"class":900}," c2.service",[252,7202,6609],{"class":262},[252,7204,7187],{"class":896},[252,7206,7207],{"class":900}," start",[252,7209,7210],{"class":900}," c2.service\n",[26,7212,7213,7214,7217,7218,126],{},"You can check the status of the service we created with the ",[249,7215,7216],{},"status"," command of ",[249,7219,7220],{},"systemctl",[242,7222,7224],{"className":886,"code":7223,"filename":888,"language":889,"meta":188,"style":188},"sudo systemctl status c2.service\n",[249,7225,7226],{"__ignoreMap":188},[252,7227,7228,7230,7232,7235],{"class":254,"line":255},[252,7229,5806],{"class":896},[252,7231,7187],{"class":900},[252,7233,7234],{"class":900}," status",[252,7236,7210],{"class":900},[26,7238,7239],{},[41,7240],{"alt":7241,"className":7242,"src":7243,"height":7244,"width":7245},"systemd service runs our c2 successfully",[45,46],"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fsystemd-service-running.webp",218,762,[458,7247,968],{"id":967},[26,7249,7250],{},"That's it, we are spies now 😂 . Everywhere we go we can implant those devices and see and read what's going on, even insert some keystrokes and cause mayhem. In the right time and the right place, it can be very valuable. But we're just playing around here and learning new stuff, because it's fun and cool.",[26,7252,7253],{},"I don't know about you, but I had a really fun time playing Q (from James Bond) for a day.",[772,7255,7256],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":188,"searchDepth":189,"depth":189,"links":7258},[7259,7260,7261,7262,7263],{"id":6338,"depth":189,"text":6339},{"id":6667,"depth":189,"text":6668},{"id":6854,"depth":189,"text":6855},{"id":6973,"depth":189,"text":6974},{"id":967,"depth":189,"text":968},"2020-10-01","Using Hak5's Screen Crab and Key Croc to remotely spy on a screen.",{"src":7267},"\u002Fposts\u002F2020\u002Flike-a-spy-with-hak5-toys\u002Fspy-cover.webp",{},"\u002Fblog\u002F2020\u002Flike-a-spy-with-hak5-toys",{"title":6301,"description":7265},"blog\u002F2020\u002Flike-a-spy-with-hak5-toys","qsoPpd9rQOB1LjX5s3WNRepIAov6kOmL3pzqONgCSGY",{"id":7274,"title":7275,"author":7276,"badge":7278,"body":7279,"date":7433,"description":7434,"draft":193,"extension":194,"image":7435,"meta":7437,"navigation":198,"path":7438,"seo":7439,"stem":7440,"__hash__":7441},"posts\u002Fblog\u002F2020\u002Fjekyll-starter-kit-generator-3.x.x.md","Jekyll Starter Kit generator 3.x.x",{"name":16,"to":17,"avatar":7277},{"src":19},{"label":4528},{"type":23,"value":7280,"toc":7426},[7281,7284,7298,7302,7305,7308,7317,7321,7324,7336,7345,7349,7418,7420,7423],[26,7282,7283],{},"First thing first, I want to send virtual thanks, gratitude and hugs to all the people that star, download, use, and contribute the project! This is huge for me, you don't know how much it affects and keeps me going. Thank you!!",[26,7285,7286,7287,7292,7293,7297],{},"And for those of you who has yet to hear about the Jekyll Starter Kit generator, it’s a ",[58,7288,7291],{"href":7289,"rel":7290},"http:\u002F\u002Fyeoman.io\u002F",[62],"Yeoman"," generator for creating ",[58,7294,4547],{"href":7295,"rel":7296},"https:\u002F\u002Fjekyllrb.com\u002F",[62]," projects with PWA support and a lot more best practices stuff.",[458,7299,7301],{"id":7300},"_1-contribution","1. Contribution",[26,7303,7304],{},"I want to call all of you who have an idea for a new feature, who have the general eagerness to contribute to open source, and the ones who have never contributed to open source. This is an easy project to get in to, and it's a pretty small codebase.",[26,7306,7307],{},"If you're new to GitHub, contribution, or even JavaScript, I would be happy to help you get started!",[26,7309,7310],{},[41,7311],{"alt":7312,"className":7313,"height":7314,"src":7315,"width":7316},"Social Coding",[45,46],138,"\u002Fposts\u002F2020\u002Fjekyll-starter-kit-generator-3.x.x\u002Fhello-world.webp",732,[458,7318,7320],{"id":7319},"_2-jekyll-starter-kit-3xx","2. Jekyll Starter Kit 3.x.x",[26,7322,7323],{},"So, what's new in version 3 is about? And why the hell we jump from 2.3.2 to 3.x.x?",[26,7325,7326,7327,7331,7332,7335],{},"Honestly there haven't been much to improve, the project is very stable and is working for quite some time now. The reason we jump from version 2.x.x to version 3.x.x is because ",[58,7328,4892],{"href":7329,"rel":7330},"https:\u002F\u002Fsemver.org\u002F",[62],". By this standard we have to bump the major when our project introduces breaking changes, and when ",[58,7333,4547],{"href":7295,"rel":7334},[62]," updated its project to version 4 they made some breaking changes (changes that force you to manually change things to be compatible with).",[26,7337,7338,7339,7344],{},"So when I updated the ",[58,7340,7343],{"href":7341,"rel":7342},"https:\u002F\u002Fgithub.com\u002Fnirgn975\u002Fgenerator-jekyll-starter-kit",[62],"Jekyll Starter Kit"," to use Jekyll version 4, I also introduced some breaking changes, to mark this in the version number I bump the major from 2 to 3.",[4760,7346,7348],{"id":7347},"_21-the-jekyll-update-is-a-major-change-but-what-else-we-have-done","2.1 The Jekyll update is a major change! But what else we have done?",[151,7350,7351,7368,7383,7396,7403,7412],{},[154,7352,7353,7354,7359,7360,7363,7364,7367],{},"npm bought ",[58,7355,7358],{"href":7356,"rel":7357},"https:\u002F\u002Fgithub.com\u002Fnodesecurity\u002Fnsp",[62],"nsp"," and incorporated it into ",[249,7361,7362],{},"npm audit",". So we moved our security check to use ",[249,7365,7366],{},"npm audit --audit-level high"," before every publish of a new release.",[154,7369,7370,7371,7376,7377,7382],{},"We removed ",[58,7372,7375],{"href":7373,"rel":7374},"https:\u002F\u002Ftravis-ci.org\u002F",[62],"Travis-CI"," and moved to ",[58,7378,7381],{"href":7379,"rel":7380},"https:\u002F\u002Fgithub.com\u002Ffeatures\u002Factions",[62],"GitHub actions"," for our CI. I think it's much easier to write and you don't need to sign in to yet another service, so it's a win win. We also add support for node version 12 in the CI and remove version 8.",[154,7384,7385,7386,7391,7392,126],{},"We also publish the package to ",[58,7387,7390],{"href":7388,"rel":7389},"https:\u002F\u002Fgithub.com\u002Ffeatures\u002Fpackages",[62],"GitHub registry"," in addition to ",[58,7393,1077],{"href":7394,"rel":7395},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fgenerator-jekyll-starter-kit",[62],[154,7397,7398,7399,7402],{},"All those changes break couple of badges in the ",[249,7400,7401],{},"README.md"," file, so we fix them and add a contribute badge so everyone will know this package is actively maintained (and this badge is good for one year, so if I stop maintaining this project it will automatically say it's not maintained).",[154,7404,7405,7406,7411],{},"Update all of the dependencies and add the ",[58,7407,7410],{"href":7408,"rel":7409},"https:\u002F\u002Fdependabot.com\u002F",[62],"dependabot"," to automate the dependencies update process. And now that we have all the tests and a full CI system on GitHub actions, it's easier and more automated than ever! You can be sure the project will be up to date without any 1-day security issues.",[154,7413,7414,7415,4769],{},"To make life easier for the contributors we added Issues and Pull requests templates. We also created ",[249,7416,7417],{},"CODE_OF_CONDUCT",[458,7419,4839],{"id":4838},[26,7421,7422],{},"The project has been updated, maintained, bugs are squashed and everything is looking good and work great! I'm really happy with its current situation.",[26,7424,7425],{},"Just one more thing. If you ever used or try this project, I'll be thrilled to get a link or hear from you how you use it, and what it helps you accomplish. I want to hear about your issues and how this project can help you solve them.",{"title":188,"searchDepth":189,"depth":189,"links":7427},[7428,7429,7432],{"id":7300,"depth":189,"text":7301},{"id":7319,"depth":189,"text":7320,"children":7430},[7431],{"id":7347,"depth":326,"text":7348},{"id":4838,"depth":189,"text":4839},"2020-09-01","The Jekyll Starter Kit generator jumps to version 3.x.x after Jekyll 4.",{"src":7436},"\u002Fposts\u002F2020\u002Fjekyll-starter-kit-generator-3.x.x\u002Fgithub-social-coding.webp",{},"\u002Fblog\u002F2020\u002Fjekyll-starter-kit-generator-3.x.x",{"title":7275,"description":7434},"blog\u002F2020\u002Fjekyll-starter-kit-generator-3.x.x","rdEVbDPTHO9rlw6FeXR5FhujPpcrPREmnPBzkT5nTZU",{"id":7443,"title":5589,"author":7444,"badge":7446,"body":7447,"date":13820,"description":13821,"draft":193,"extension":194,"image":13822,"meta":13824,"navigation":198,"path":13825,"seo":13826,"stem":13827,"__hash__":13828},"posts\u002Fblog\u002F2020\u002Fopen-source-intelligence.md",{"name":16,"to":17,"avatar":7445},{"src":19},{"label":5580},{"type":23,"value":7448,"toc":13810},[7449,7452,7463,7466,7473,7476,7484,7488,7491,7494,7497,7609,7612,7640,7644,7663,8407,8410,9709,9722,9761,9770,9773,9791,9794,11543,11546,11554,11567,11573,11579,11583,11592,11599,11612,11645,11648,11677,11686,11695,11704,11713,11728,11737,11755,11768,11771,11793,11801,11809,11813,11831,11840,11843,11852,11891,11908,11917,11926,11939,11946,11949,11975,11978,11982,11994,12190,12199,12208,12218,12350,12357,12370,12383,12663,12685,12707,12720,12727,12733,12737,12749,13440,13469,13477,13516,13532,13539,13552,13558,13569,13618,13627,13638,13645,13652,13658,13665,13672,13722,13749,13753,13756,13765,13772,13790,13797,13801,13804,13807],[26,7450,7451],{},"Every operation need good intel, and good intel is hard to find. Or is it?",[26,7453,7454,7459,7460,126],{},[58,7455,7458],{"href":7456,"rel":7457},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FOpen-source_intelligence",[62],"OSINT (or Open Source Intelligence)"," is the operation of collecting and analyzing information about a target from various sources. A lot of times you'll see the terms \"public\" or \"open\" sources but let's be honest here, this is false and that's why I wrote ",[951,7461,7462],{},"various sources",[26,7464,7465],{},"This is not to say that hacking to someone and steal his data is OSINT, because it's not. But not all the sources we'll use are public and open to everyone, and their data isn't collected by those services in an open way (innocently and \u002F or with user consent).",[26,7467,7468,7469,7472],{},"When talking about reconnaissance, the first step of red teams in an engagement (or 5 phases of ",[252,7470,7471],{},"ethical"," hacking), people means passive reconnaissance. It's the attempt to gain information about a target (a human, a computer and \u002F or a network) without actively engaging with it.",[26,7474,7475],{},"In this post I want you to tag a long with me when I'm doing a reconnaissance. Maybe I will censor certain details but I'll talk about it all. There are a lot of tools to help us do it, and we'll use them, but none the less there is a lot of manual hard work (and drop of art) to be done.",[6344,7477,7478],{},[26,7479,7480,7483],{},[871,7481,7482],{},"Disclaimer:"," I'm not a professional pen tester. This is for educational purposes only.",[458,7485,7487],{"id":7486},"_1-story-time","1. Story Time",[26,7489,7490],{},"At work I am responsible for a service that is mostly a backend service, but we have a small client (web interface) to help us manage things and see some analytics about our company's product. This interface have a login page where you need to enter a username and a password to get in, and you can't register to it, I need to give you access to it. This is an internal tool for the company.",[26,7492,7493],{},"So, naturally, I put some more security on that service. One of them was to log all logins to the system, and especially false attempts to login and an email notification to myself when that happens.",[26,7495,7496],{},"Everything was quiet for a long time, but we know it's not going to be like that forever. Lo and behold about a month ago it happened for the first time! Someone tried to penetrate the system with a non-existent username and password. I got 4 attempts, because I don't even let you have a third strike - I put your IP in a blacklist. So as you see in the table below we basically have 2 real attempts (2 IPs) with 4 different credentials.",[7498,7499,7500,7525],"table",{},[7501,7502,7503],"thead",{},[7504,7505,7506,7510,7513,7516,7519,7522],"tr",{},[7507,7508,7509],"th",{},"Email",[7507,7511,7512],{},"Password",[7507,7514,7515],{},"IP",[7507,7517,7518],{},"Time",[7507,7520,7521],{},"Accept Language",[7507,7523,7524],{},"User Agent",[7526,7527,7528,7552,7570,7591],"tbody",{},[7504,7529,7530,7537,7540,7543,7546,7549],{},[7531,7532,7533],"td",{},[58,7534,7536],{"href":7535},"mailto:deion_kihn@bocah.team","deion_kihn@bocah.team",[7531,7538,7539],{},"Harry1982!",[7531,7541,7542],{},"84.17.46.157",[7531,7544,7545],{},"2020-05-27T09:16:44.044Z",[7531,7547,7548],{},"en-US",[7531,7550,7551],{},"Mozilla\u002F5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\u002F537.36 (KHTML, like Gecko) Chrome\u002F67.0.3396.99 Safari\u002F537.36",[7504,7553,7554,7558,7561,7563,7566,7568],{},[7531,7555,7556],{},[58,7557,7536],{"href":7535},[7531,7559,7560],{},"Harry1945",[7531,7562,7542],{},[7531,7564,7565],{},"2020-05-27T09:17:08.923Z",[7531,7567,7548],{},[7531,7569,7551],{},[7504,7571,7572,7578,7581,7584,7587,7589],{},[7531,7573,7574],{},[58,7575,7577],{"href":7576},"mailto:zena.durgan91@bocah.team","zena.durgan91@bocah.team",[7531,7579,7580],{},"Isaac2007",[7531,7582,7583],{},"154.127.57.238",[7531,7585,7586],{},"2020-05-27T11:26:31.250Z",[7531,7588,7548],{},[7531,7590,7551],{},[7504,7592,7593,7597,7600,7602,7605,7607],{},[7531,7594,7595],{},[58,7596,7577],{"href":7576},[7531,7598,7599],{},"Isaac1964!",[7531,7601,7583],{},[7531,7603,7604],{},"2020-05-27T11:26:55.474Z",[7531,7606,7548],{},[7531,7608,7551],{},[26,7610,7611],{},"We notice a bunch of things:",[151,7613,7614,7617,7620,7623,7633],{},[154,7615,7616],{},"We have only 2 IPs.",[154,7618,7619],{},"The email address doesn't change in every attempt, only the password is changing in each attempt.",[154,7621,7622],{},"The two emails are with the same domain.",[154,7624,7625,7626,7628,7629,7632],{},"The language is always ",[249,7627,7548],{}," which says nothing - this is by far the popular ",[249,7630,7631],{},"accept-language"," header on the internet.",[154,7634,7635,7636,7639],{},"The ",[249,7637,7638],{},"user agent"," is from Windows 10 with a Chrome web browser version 67 (the same exact version for all four attempts), which is pretty old version (and remember that chrome is automatically updated).",[458,7641,7643],{"id":7642},"_2-lets-start-digging","2. Let's Start Digging",[26,7645,7646,7647,7649,7650,7652,7653,7656,7657,7662],{},"The easiest thing to look up first are the IPs and the domain. The first IP (",[249,7648,7542],{},") is from Netherlands, and the second (",[249,7651,7583],{},") is from the South Africa, here are the full ",[249,7654,7655],{},"JSON","s files I got from ",[58,7658,7661],{"href":7659,"rel":7660},"https:\u002F\u002Fipstack.com",[62],"ipstack",". I got the paid version of their API, but there is a free one and that's enough for our case here.",[242,7664,7667],{"className":3031,"code":7665,"filename":7666,"language":3034,"meta":188,"style":188},"{\n  \"ip\": \"84.17.46.157\",\n  \"type\": \"ipv4\",\n  \"continent_code\": \"EU\",\n  \"continent_name\": \"Europe\",\n  \"country_code\": \"NL\",\n  \"country_name\": \"Netherlands\",\n  \"region_code\": \"NH\",\n  \"region_name\": \"North Holland\",\n  \"city\": \"Diemen\",\n  \"zip\": \"1101\",\n  \"latitude\": 52.309051513671875,\n  \"longitude\": 4.940189838409424,\n  \"location\": {\n    \"geoname_id\": 2756888,\n    \"capital\": \"Amsterdam\",\n    \"languages\": [\n      {\n        \"code\": \"nl\",\n        \"name\": \"Dutch\",\n        \"native\": \"Nederlands\"\n      }\n    ],\n    \"country_flag\": \"http:\u002F\u002Fassets.ipstack.com\u002Fflags\u002Fnl.svg\",\n    \"country_flag_emoji\": \"\\ud83c\\uddf3\\ud83c\\uddf1\",\n    \"country_flag_emoji_unicode\": \"U+1F1F3 U+1F1F1\",\n    \"calling_code\": \"31\",\n    \"is_eu\": true\n  },\n  \"time_zone\": {\n    \"id\": \"Europe\u002FAmsterdam\",\n    \"current_time\": \"2020-05-02T15:15:21+02:00\",\n    \"gmt_offset\": 7200,\n    \"code\": \"CEST\",\n    \"is_daylight_saving\": true\n  },\n  \"currency\": {\n    \"code\": \"EUR\",\n    \"name\": \"Euro\",\n    \"plural\": \"euros\",\n    \"symbol\": \"\\u20ac\",\n    \"symbol_native\": \"\\u20ac\"\n  },\n  \"connection\": {\n    \"asn\": 60068,\n    \"isp\": \"Datacamp Limited\"\n  }\n}\n","ipstack-1.json",[249,7668,7669,7673,7692,7711,7731,7751,7771,7791,7811,7831,7851,7870,7886,7902,7915,7931,7951,7964,7969,7989,8008,8026,8031,8036,8056,8076,8096,8116,8129,8133,8146,8166,8186,8202,8221,8234,8238,8251,8270,8289,8309,8329,8346,8350,8363,8379,8397,8402],{"__ignoreMap":188},[252,7670,7671],{"class":254,"line":255},[252,7672,303],{"class":262},[252,7674,7675,7677,7680,7682,7684,7686,7688,7690],{"class":254,"line":189},[252,7676,3056],{"class":262},[252,7678,7679],{"class":258},"ip",[252,7681,1266],{"class":262},[252,7683,312],{"class":262},[252,7685,1471],{"class":262},[252,7687,7542],{"class":900},[252,7689,1266],{"class":262},[252,7691,323],{"class":262},[252,7693,7694,7696,7698,7700,7702,7704,7707,7709],{"class":254,"line":326},[252,7695,3056],{"class":262},[252,7697,1231],{"class":258},[252,7699,1266],{"class":262},[252,7701,312],{"class":262},[252,7703,1471],{"class":262},[252,7705,7706],{"class":900},"ipv4",[252,7708,1266],{"class":262},[252,7710,323],{"class":262},[252,7712,7713,7715,7718,7720,7722,7724,7727,7729],{"class":254,"line":343},[252,7714,3056],{"class":262},[252,7716,7717],{"class":258},"continent_code",[252,7719,1266],{"class":262},[252,7721,312],{"class":262},[252,7723,1471],{"class":262},[252,7725,7726],{"class":900},"EU",[252,7728,1266],{"class":262},[252,7730,323],{"class":262},[252,7732,7733,7735,7738,7740,7742,7744,7747,7749],{"class":254,"line":354},[252,7734,3056],{"class":262},[252,7736,7737],{"class":258},"continent_name",[252,7739,1266],{"class":262},[252,7741,312],{"class":262},[252,7743,1471],{"class":262},[252,7745,7746],{"class":900},"Europe",[252,7748,1266],{"class":262},[252,7750,323],{"class":262},[252,7752,7753,7755,7758,7760,7762,7764,7767,7769],{"class":254,"line":364},[252,7754,3056],{"class":262},[252,7756,7757],{"class":258},"country_code",[252,7759,1266],{"class":262},[252,7761,312],{"class":262},[252,7763,1471],{"class":262},[252,7765,7766],{"class":900},"NL",[252,7768,1266],{"class":262},[252,7770,323],{"class":262},[252,7772,7773,7775,7778,7780,7782,7784,7787,7789],{"class":254,"line":381},[252,7774,3056],{"class":262},[252,7776,7777],{"class":258},"country_name",[252,7779,1266],{"class":262},[252,7781,312],{"class":262},[252,7783,1471],{"class":262},[252,7785,7786],{"class":900},"Netherlands",[252,7788,1266],{"class":262},[252,7790,323],{"class":262},[252,7792,7793,7795,7798,7800,7802,7804,7807,7809],{"class":254,"line":398},[252,7794,3056],{"class":262},[252,7796,7797],{"class":258},"region_code",[252,7799,1266],{"class":262},[252,7801,312],{"class":262},[252,7803,1471],{"class":262},[252,7805,7806],{"class":900},"NH",[252,7808,1266],{"class":262},[252,7810,323],{"class":262},[252,7812,7813,7815,7818,7820,7822,7824,7827,7829],{"class":254,"line":415},[252,7814,3056],{"class":262},[252,7816,7817],{"class":258},"region_name",[252,7819,1266],{"class":262},[252,7821,312],{"class":262},[252,7823,1471],{"class":262},[252,7825,7826],{"class":900},"North Holland",[252,7828,1266],{"class":262},[252,7830,323],{"class":262},[252,7832,7833,7835,7838,7840,7842,7844,7847,7849],{"class":254,"line":421},[252,7834,3056],{"class":262},[252,7836,7837],{"class":258},"city",[252,7839,1266],{"class":262},[252,7841,312],{"class":262},[252,7843,1471],{"class":262},[252,7845,7846],{"class":900},"Diemen",[252,7848,1266],{"class":262},[252,7850,323],{"class":262},[252,7852,7853,7855,7857,7859,7861,7863,7866,7868],{"class":254,"line":427},[252,7854,3056],{"class":262},[252,7856,6553],{"class":258},[252,7858,1266],{"class":262},[252,7860,312],{"class":262},[252,7862,1471],{"class":262},[252,7864,7865],{"class":900},"1101",[252,7867,1266],{"class":262},[252,7869,323],{"class":262},[252,7871,7872,7874,7877,7879,7881,7884],{"class":254,"line":662},[252,7873,3056],{"class":262},[252,7875,7876],{"class":258},"latitude",[252,7878,1266],{"class":262},[252,7880,312],{"class":262},[252,7882,7883],{"class":1782}," 52.309051513671875",[252,7885,323],{"class":262},[252,7887,7888,7890,7893,7895,7897,7900],{"class":254,"line":668},[252,7889,3056],{"class":262},[252,7891,7892],{"class":258},"longitude",[252,7894,1266],{"class":262},[252,7896,312],{"class":262},[252,7898,7899],{"class":1782}," 4.940189838409424",[252,7901,323],{"class":262},[252,7903,7904,7906,7909,7911,7913],{"class":254,"line":674},[252,7905,3056],{"class":262},[252,7907,7908],{"class":258},"location",[252,7910,1266],{"class":262},[252,7912,312],{"class":262},[252,7914,351],{"class":262},[252,7916,7917,7919,7922,7924,7926,7929],{"class":254,"line":1414},[252,7918,1495],{"class":262},[252,7920,7921],{"class":896},"geoname_id",[252,7923,1266],{"class":262},[252,7925,312],{"class":262},[252,7927,7928],{"class":1782}," 2756888",[252,7930,323],{"class":262},[252,7932,7933,7935,7938,7940,7942,7944,7947,7949],{"class":254,"line":1610},[252,7934,1495],{"class":262},[252,7936,7937],{"class":896},"capital",[252,7939,1266],{"class":262},[252,7941,312],{"class":262},[252,7943,1471],{"class":262},[252,7945,7946],{"class":900},"Amsterdam",[252,7948,1266],{"class":262},[252,7950,323],{"class":262},[252,7952,7953,7955,7958,7960,7962],{"class":254,"line":1618},[252,7954,1495],{"class":262},[252,7956,7957],{"class":896},"languages",[252,7959,1266],{"class":262},[252,7961,312],{"class":262},[252,7963,1490],{"class":262},[252,7965,7966],{"class":254,"line":1628},[252,7967,7968],{"class":262},"      {\n",[252,7970,7971,7974,7976,7978,7980,7982,7985,7987],{"class":254,"line":1645},[252,7972,7973],{"class":262},"        \"",[252,7975,249],{"class":1782},[252,7977,1266],{"class":262},[252,7979,312],{"class":262},[252,7981,1471],{"class":262},[252,7983,7984],{"class":900},"nl",[252,7986,1266],{"class":262},[252,7988,323],{"class":262},[252,7990,7991,7993,7995,7997,7999,8001,8004,8006],{"class":254,"line":1655},[252,7992,7973],{"class":262},[252,7994,3536],{"class":1782},[252,7996,1266],{"class":262},[252,7998,312],{"class":262},[252,8000,1471],{"class":262},[252,8002,8003],{"class":900},"Dutch",[252,8005,1266],{"class":262},[252,8007,323],{"class":262},[252,8009,8010,8012,8015,8017,8019,8021,8024],{"class":254,"line":1660},[252,8011,7973],{"class":262},[252,8013,8014],{"class":1782},"native",[252,8016,1266],{"class":262},[252,8018,312],{"class":262},[252,8020,1471],{"class":262},[252,8022,8023],{"class":900},"Nederlands",[252,8025,3062],{"class":262},[252,8027,8028],{"class":254,"line":2363},[252,8029,8030],{"class":262},"      }\n",[252,8032,8033],{"class":254,"line":2392},[252,8034,8035],{"class":262},"    ],\n",[252,8037,8038,8040,8043,8045,8047,8049,8052,8054],{"class":254,"line":2407},[252,8039,1495],{"class":262},[252,8041,8042],{"class":896},"country_flag",[252,8044,1266],{"class":262},[252,8046,312],{"class":262},[252,8048,1471],{"class":262},[252,8050,8051],{"class":900},"http:\u002F\u002Fassets.ipstack.com\u002Fflags\u002Fnl.svg",[252,8053,1266],{"class":262},[252,8055,323],{"class":262},[252,8057,8058,8060,8063,8065,8067,8069,8072,8074],{"class":254,"line":2421},[252,8059,1495],{"class":262},[252,8061,8062],{"class":896},"country_flag_emoji",[252,8064,1266],{"class":262},[252,8066,312],{"class":262},[252,8068,1471],{"class":262},[252,8070,8071],{"class":266},"\\ud83c\\uddf3\\ud83c\\uddf1",[252,8073,1266],{"class":262},[252,8075,323],{"class":262},[252,8077,8078,8080,8083,8085,8087,8089,8092,8094],{"class":254,"line":2436},[252,8079,1495],{"class":262},[252,8081,8082],{"class":896},"country_flag_emoji_unicode",[252,8084,1266],{"class":262},[252,8086,312],{"class":262},[252,8088,1471],{"class":262},[252,8090,8091],{"class":900},"U+1F1F3 U+1F1F1",[252,8093,1266],{"class":262},[252,8095,323],{"class":262},[252,8097,8098,8100,8103,8105,8107,8109,8112,8114],{"class":254,"line":2441},[252,8099,1495],{"class":262},[252,8101,8102],{"class":896},"calling_code",[252,8104,1266],{"class":262},[252,8106,312],{"class":262},[252,8108,1471],{"class":262},[252,8110,8111],{"class":900},"31",[252,8113,1266],{"class":262},[252,8115,323],{"class":262},[252,8117,8118,8120,8123,8125,8127],{"class":254,"line":2446},[252,8119,1495],{"class":262},[252,8121,8122],{"class":896},"is_eu",[252,8124,1266],{"class":262},[252,8126,312],{"class":262},[252,8128,3439],{"class":262},[252,8130,8131],{"class":254,"line":2472},[252,8132,424],{"class":262},[252,8134,8135,8137,8140,8142,8144],{"class":254,"line":2486},[252,8136,3056],{"class":262},[252,8138,8139],{"class":258},"time_zone",[252,8141,1266],{"class":262},[252,8143,312],{"class":262},[252,8145,351],{"class":262},[252,8147,8148,8150,8153,8155,8157,8159,8162,8164],{"class":254,"line":2501},[252,8149,1495],{"class":262},[252,8151,8152],{"class":896},"id",[252,8154,1266],{"class":262},[252,8156,312],{"class":262},[252,8158,1471],{"class":262},[252,8160,8161],{"class":900},"Europe\u002FAmsterdam",[252,8163,1266],{"class":262},[252,8165,323],{"class":262},[252,8167,8168,8170,8173,8175,8177,8179,8182,8184],{"class":254,"line":3002},[252,8169,1495],{"class":262},[252,8171,8172],{"class":896},"current_time",[252,8174,1266],{"class":262},[252,8176,312],{"class":262},[252,8178,1471],{"class":262},[252,8180,8181],{"class":900},"2020-05-02T15:15:21+02:00",[252,8183,1266],{"class":262},[252,8185,323],{"class":262},[252,8187,8188,8190,8193,8195,8197,8200],{"class":254,"line":3007},[252,8189,1495],{"class":262},[252,8191,8192],{"class":896},"gmt_offset",[252,8194,1266],{"class":262},[252,8196,312],{"class":262},[252,8198,8199],{"class":1782}," 7200",[252,8201,323],{"class":262},[252,8203,8204,8206,8208,8210,8212,8214,8217,8219],{"class":254,"line":3012},[252,8205,1495],{"class":262},[252,8207,249],{"class":896},[252,8209,1266],{"class":262},[252,8211,312],{"class":262},[252,8213,1471],{"class":262},[252,8215,8216],{"class":900},"CEST",[252,8218,1266],{"class":262},[252,8220,323],{"class":262},[252,8222,8223,8225,8228,8230,8232],{"class":254,"line":3805},[252,8224,1495],{"class":262},[252,8226,8227],{"class":896},"is_daylight_saving",[252,8229,1266],{"class":262},[252,8231,312],{"class":262},[252,8233,3439],{"class":262},[252,8235,8236],{"class":254,"line":3810},[252,8237,424],{"class":262},[252,8239,8240,8242,8245,8247,8249],{"class":254,"line":3822},[252,8241,3056],{"class":262},[252,8243,8244],{"class":258},"currency",[252,8246,1266],{"class":262},[252,8248,312],{"class":262},[252,8250,351],{"class":262},[252,8252,8253,8255,8257,8259,8261,8263,8266,8268],{"class":254,"line":3832},[252,8254,1495],{"class":262},[252,8256,249],{"class":896},[252,8258,1266],{"class":262},[252,8260,312],{"class":262},[252,8262,1471],{"class":262},[252,8264,8265],{"class":900},"EUR",[252,8267,1266],{"class":262},[252,8269,323],{"class":262},[252,8271,8272,8274,8276,8278,8280,8282,8285,8287],{"class":254,"line":3840},[252,8273,1495],{"class":262},[252,8275,3536],{"class":896},[252,8277,1266],{"class":262},[252,8279,312],{"class":262},[252,8281,1471],{"class":262},[252,8283,8284],{"class":900},"Euro",[252,8286,1266],{"class":262},[252,8288,323],{"class":262},[252,8290,8291,8293,8296,8298,8300,8302,8305,8307],{"class":254,"line":3851},[252,8292,1495],{"class":262},[252,8294,8295],{"class":896},"plural",[252,8297,1266],{"class":262},[252,8299,312],{"class":262},[252,8301,1471],{"class":262},[252,8303,8304],{"class":900},"euros",[252,8306,1266],{"class":262},[252,8308,323],{"class":262},[252,8310,8311,8313,8316,8318,8320,8322,8325,8327],{"class":254,"line":3858},[252,8312,1495],{"class":262},[252,8314,8315],{"class":896},"symbol",[252,8317,1266],{"class":262},[252,8319,312],{"class":262},[252,8321,1471],{"class":262},[252,8323,8324],{"class":266},"\\u20ac",[252,8326,1266],{"class":262},[252,8328,323],{"class":262},[252,8330,8331,8333,8336,8338,8340,8342,8344],{"class":254,"line":3874},[252,8332,1495],{"class":262},[252,8334,8335],{"class":896},"symbol_native",[252,8337,1266],{"class":262},[252,8339,312],{"class":262},[252,8341,1471],{"class":262},[252,8343,8324],{"class":266},[252,8345,3062],{"class":262},[252,8347,8348],{"class":254,"line":3884},[252,8349,424],{"class":262},[252,8351,8352,8354,8357,8359,8361],{"class":254,"line":3889},[252,8353,3056],{"class":262},[252,8355,8356],{"class":258},"connection",[252,8358,1266],{"class":262},[252,8360,312],{"class":262},[252,8362,351],{"class":262},[252,8364,8365,8367,8370,8372,8374,8377],{"class":254,"line":3897},[252,8366,1495],{"class":262},[252,8368,8369],{"class":896},"asn",[252,8371,1266],{"class":262},[252,8373,312],{"class":262},[252,8375,8376],{"class":1782}," 60068",[252,8378,323],{"class":262},[252,8380,8381,8383,8386,8388,8390,8392,8395],{"class":254,"line":3907},[252,8382,1495],{"class":262},[252,8384,8385],{"class":896},"isp",[252,8387,1266],{"class":262},[252,8389,312],{"class":262},[252,8391,1471],{"class":262},[252,8393,8394],{"class":900},"Datacamp Limited",[252,8396,3062],{"class":262},[252,8398,8399],{"class":254,"line":3916},[252,8400,8401],{"class":262},"  }\n",[252,8403,8404],{"class":254,"line":3924},[252,8405,8406],{"class":262},"}\n",[26,8408,8409],{},"and",[242,8411,8414],{"className":3031,"code":8412,"filename":8413,"language":3034,"meta":188,"style":188},"{\n  \"ip\": \"154.127.57.238\",\n  \"type\": \"ipv4\",\n  \"continent_code\": \"AF\",\n  \"continent_name\": \"Africa\",\n  \"country_code\": \"ZA\",\n  \"country_name\": \"South Africa\",\n  \"region_code\": \"GT\",\n  \"region_name\": \"Gauteng\",\n  \"city\": \"Johannesburg\",\n  \"zip\": \"2000\",\n  \"latitude\": -26.199169158935547,\n  \"longitude\": 28.0563907623291,\n  \"location\": {\n    \"geoname_id\": 993800,\n    \"capital\": \"Pretoria\",\n    \"languages\": [\n      {\n        \"code\": \"af\",\n        \"name\": \"Afrikaans\",\n        \"native\": \"Afrikaans\"\n      },\n      {\n        \"code\": \"en\",\n        \"name\": \"English\",\n        \"native\": \"English\"\n      },\n      {\n        \"code\": \"nr\",\n        \"name\": \"South Ndebele\",\n        \"native\": \"isiNdebele\"\n      },\n      {\n        \"code\": \"st\",\n        \"name\": \"Southern Sotho\",\n        \"native\": \"Sesotho\"\n      },\n      {\n        \"code\": \"ss\",\n        \"name\": \"Swati\",\n        \"native\": \"SiSwati\"\n      },\n      {\n        \"code\": \"tn\",\n        \"name\": \"Tswana\",\n        \"native\": \"Setswana\"\n      },\n      {\n        \"code\": \"ts\",\n        \"name\": \"Tsonga\",\n        \"native\": \"Xitsonga\"\n      },\n      {\n        \"code\": \"ve\",\n        \"name\": \"Venda\",\n        \"native\": \"Tshiven\\u1e13a\"\n      },\n      {\n        \"code\": \"xh\",\n        \"name\": \"Xhosa\",\n        \"native\": \"isiXhosa\"\n      },\n      {\n        \"code\": \"zu\",\n        \"name\": \"Zulu\",\n        \"native\": \"isiZulu\"\n      }\n    ],\n    \"country_flag\": \"http:\u002F\u002Fassets.ipstack.com\u002Fflags\u002Fza.svg\",\n    \"country_flag_emoji\": \"\\ud83c\\uddff\\ud83c\\udde6\",\n    \"country_flag_emoji_unicode\": \"U+1F1FF U+1F1E6\",\n    \"calling_code\": \"27\",\n    \"is_eu\": false\n  },\n  \"time_zone\": {\n    \"id\": \"Africa\u002FJohannesburg\",\n    \"current_time\": \"2020-05-02T15:20:17+02:00\",\n    \"gmt_offset\": 7200,\n    \"code\": \"SAST\",\n    \"is_daylight_saving\": false\n  },\n  \"currency\": {\n    \"code\": \"ZAR\",\n    \"name\": \"South African Rand\",\n    \"plural\": \"South African rand\",\n    \"symbol\": \"R\",\n    \"symbol_native\": \"R\"\n  },\n  \"connection\": {\n    \"asn\": 61317,\n    \"isp\": \"Digital Energy Technologies Ltd.\"\n  }\n}\n","ip-stack2.json",[249,8415,8416,8420,8438,8456,8475,8494,8513,8532,8551,8570,8589,8608,8623,8638,8650,8665,8684,8696,8700,8719,8738,8754,8758,8762,8781,8800,8816,8820,8824,8843,8862,8879,8883,8887,8906,8925,8942,8946,8950,8969,8988,9005,9009,9013,9032,9051,9068,9072,9076,9094,9113,9130,9134,9138,9157,9176,9198,9202,9206,9225,9244,9261,9265,9269,9288,9307,9324,9328,9333,9353,9373,9393,9413,9427,9432,9445,9465,9485,9500,9520,9533,9538,9551,9571,9591,9611,9630,9647,9652,9665,9681,9699,9704],{"__ignoreMap":188},[252,8417,8418],{"class":254,"line":255},[252,8419,303],{"class":262},[252,8421,8422,8424,8426,8428,8430,8432,8434,8436],{"class":254,"line":189},[252,8423,3056],{"class":262},[252,8425,7679],{"class":258},[252,8427,1266],{"class":262},[252,8429,312],{"class":262},[252,8431,1471],{"class":262},[252,8433,7583],{"class":900},[252,8435,1266],{"class":262},[252,8437,323],{"class":262},[252,8439,8440,8442,8444,8446,8448,8450,8452,8454],{"class":254,"line":326},[252,8441,3056],{"class":262},[252,8443,1231],{"class":258},[252,8445,1266],{"class":262},[252,8447,312],{"class":262},[252,8449,1471],{"class":262},[252,8451,7706],{"class":900},[252,8453,1266],{"class":262},[252,8455,323],{"class":262},[252,8457,8458,8460,8462,8464,8466,8468,8471,8473],{"class":254,"line":343},[252,8459,3056],{"class":262},[252,8461,7717],{"class":258},[252,8463,1266],{"class":262},[252,8465,312],{"class":262},[252,8467,1471],{"class":262},[252,8469,8470],{"class":900},"AF",[252,8472,1266],{"class":262},[252,8474,323],{"class":262},[252,8476,8477,8479,8481,8483,8485,8487,8490,8492],{"class":254,"line":354},[252,8478,3056],{"class":262},[252,8480,7737],{"class":258},[252,8482,1266],{"class":262},[252,8484,312],{"class":262},[252,8486,1471],{"class":262},[252,8488,8489],{"class":900},"Africa",[252,8491,1266],{"class":262},[252,8493,323],{"class":262},[252,8495,8496,8498,8500,8502,8504,8506,8509,8511],{"class":254,"line":364},[252,8497,3056],{"class":262},[252,8499,7757],{"class":258},[252,8501,1266],{"class":262},[252,8503,312],{"class":262},[252,8505,1471],{"class":262},[252,8507,8508],{"class":900},"ZA",[252,8510,1266],{"class":262},[252,8512,323],{"class":262},[252,8514,8515,8517,8519,8521,8523,8525,8528,8530],{"class":254,"line":381},[252,8516,3056],{"class":262},[252,8518,7777],{"class":258},[252,8520,1266],{"class":262},[252,8522,312],{"class":262},[252,8524,1471],{"class":262},[252,8526,8527],{"class":900},"South Africa",[252,8529,1266],{"class":262},[252,8531,323],{"class":262},[252,8533,8534,8536,8538,8540,8542,8544,8547,8549],{"class":254,"line":398},[252,8535,3056],{"class":262},[252,8537,7797],{"class":258},[252,8539,1266],{"class":262},[252,8541,312],{"class":262},[252,8543,1471],{"class":262},[252,8545,8546],{"class":900},"GT",[252,8548,1266],{"class":262},[252,8550,323],{"class":262},[252,8552,8553,8555,8557,8559,8561,8563,8566,8568],{"class":254,"line":415},[252,8554,3056],{"class":262},[252,8556,7817],{"class":258},[252,8558,1266],{"class":262},[252,8560,312],{"class":262},[252,8562,1471],{"class":262},[252,8564,8565],{"class":900},"Gauteng",[252,8567,1266],{"class":262},[252,8569,323],{"class":262},[252,8571,8572,8574,8576,8578,8580,8582,8585,8587],{"class":254,"line":421},[252,8573,3056],{"class":262},[252,8575,7837],{"class":258},[252,8577,1266],{"class":262},[252,8579,312],{"class":262},[252,8581,1471],{"class":262},[252,8583,8584],{"class":900},"Johannesburg",[252,8586,1266],{"class":262},[252,8588,323],{"class":262},[252,8590,8591,8593,8595,8597,8599,8601,8604,8606],{"class":254,"line":427},[252,8592,3056],{"class":262},[252,8594,6553],{"class":258},[252,8596,1266],{"class":262},[252,8598,312],{"class":262},[252,8600,1471],{"class":262},[252,8602,8603],{"class":900},"2000",[252,8605,1266],{"class":262},[252,8607,323],{"class":262},[252,8609,8610,8612,8614,8616,8618,8621],{"class":254,"line":662},[252,8611,3056],{"class":262},[252,8613,7876],{"class":258},[252,8615,1266],{"class":262},[252,8617,312],{"class":262},[252,8619,8620],{"class":1782}," -26.199169158935547",[252,8622,323],{"class":262},[252,8624,8625,8627,8629,8631,8633,8636],{"class":254,"line":668},[252,8626,3056],{"class":262},[252,8628,7892],{"class":258},[252,8630,1266],{"class":262},[252,8632,312],{"class":262},[252,8634,8635],{"class":1782}," 28.0563907623291",[252,8637,323],{"class":262},[252,8639,8640,8642,8644,8646,8648],{"class":254,"line":674},[252,8641,3056],{"class":262},[252,8643,7908],{"class":258},[252,8645,1266],{"class":262},[252,8647,312],{"class":262},[252,8649,351],{"class":262},[252,8651,8652,8654,8656,8658,8660,8663],{"class":254,"line":1414},[252,8653,1495],{"class":262},[252,8655,7921],{"class":896},[252,8657,1266],{"class":262},[252,8659,312],{"class":262},[252,8661,8662],{"class":1782}," 993800",[252,8664,323],{"class":262},[252,8666,8667,8669,8671,8673,8675,8677,8680,8682],{"class":254,"line":1610},[252,8668,1495],{"class":262},[252,8670,7937],{"class":896},[252,8672,1266],{"class":262},[252,8674,312],{"class":262},[252,8676,1471],{"class":262},[252,8678,8679],{"class":900},"Pretoria",[252,8681,1266],{"class":262},[252,8683,323],{"class":262},[252,8685,8686,8688,8690,8692,8694],{"class":254,"line":1618},[252,8687,1495],{"class":262},[252,8689,7957],{"class":896},[252,8691,1266],{"class":262},[252,8693,312],{"class":262},[252,8695,1490],{"class":262},[252,8697,8698],{"class":254,"line":1628},[252,8699,7968],{"class":262},[252,8701,8702,8704,8706,8708,8710,8712,8715,8717],{"class":254,"line":1645},[252,8703,7973],{"class":262},[252,8705,249],{"class":1782},[252,8707,1266],{"class":262},[252,8709,312],{"class":262},[252,8711,1471],{"class":262},[252,8713,8714],{"class":900},"af",[252,8716,1266],{"class":262},[252,8718,323],{"class":262},[252,8720,8721,8723,8725,8727,8729,8731,8734,8736],{"class":254,"line":1655},[252,8722,7973],{"class":262},[252,8724,3536],{"class":1782},[252,8726,1266],{"class":262},[252,8728,312],{"class":262},[252,8730,1471],{"class":262},[252,8732,8733],{"class":900},"Afrikaans",[252,8735,1266],{"class":262},[252,8737,323],{"class":262},[252,8739,8740,8742,8744,8746,8748,8750,8752],{"class":254,"line":1660},[252,8741,7973],{"class":262},[252,8743,8014],{"class":1782},[252,8745,1266],{"class":262},[252,8747,312],{"class":262},[252,8749,1471],{"class":262},[252,8751,8733],{"class":900},[252,8753,3062],{"class":262},[252,8755,8756],{"class":254,"line":2363},[252,8757,1602],{"class":262},[252,8759,8760],{"class":254,"line":2392},[252,8761,7968],{"class":262},[252,8763,8764,8766,8768,8770,8772,8774,8777,8779],{"class":254,"line":2407},[252,8765,7973],{"class":262},[252,8767,249],{"class":1782},[252,8769,1266],{"class":262},[252,8771,312],{"class":262},[252,8773,1471],{"class":262},[252,8775,8776],{"class":900},"en",[252,8778,1266],{"class":262},[252,8780,323],{"class":262},[252,8782,8783,8785,8787,8789,8791,8793,8796,8798],{"class":254,"line":2421},[252,8784,7973],{"class":262},[252,8786,3536],{"class":1782},[252,8788,1266],{"class":262},[252,8790,312],{"class":262},[252,8792,1471],{"class":262},[252,8794,8795],{"class":900},"English",[252,8797,1266],{"class":262},[252,8799,323],{"class":262},[252,8801,8802,8804,8806,8808,8810,8812,8814],{"class":254,"line":2436},[252,8803,7973],{"class":262},[252,8805,8014],{"class":1782},[252,8807,1266],{"class":262},[252,8809,312],{"class":262},[252,8811,1471],{"class":262},[252,8813,8795],{"class":900},[252,8815,3062],{"class":262},[252,8817,8818],{"class":254,"line":2441},[252,8819,1602],{"class":262},[252,8821,8822],{"class":254,"line":2446},[252,8823,7968],{"class":262},[252,8825,8826,8828,8830,8832,8834,8836,8839,8841],{"class":254,"line":2472},[252,8827,7973],{"class":262},[252,8829,249],{"class":1782},[252,8831,1266],{"class":262},[252,8833,312],{"class":262},[252,8835,1471],{"class":262},[252,8837,8838],{"class":900},"nr",[252,8840,1266],{"class":262},[252,8842,323],{"class":262},[252,8844,8845,8847,8849,8851,8853,8855,8858,8860],{"class":254,"line":2486},[252,8846,7973],{"class":262},[252,8848,3536],{"class":1782},[252,8850,1266],{"class":262},[252,8852,312],{"class":262},[252,8854,1471],{"class":262},[252,8856,8857],{"class":900},"South Ndebele",[252,8859,1266],{"class":262},[252,8861,323],{"class":262},[252,8863,8864,8866,8868,8870,8872,8874,8877],{"class":254,"line":2501},[252,8865,7973],{"class":262},[252,8867,8014],{"class":1782},[252,8869,1266],{"class":262},[252,8871,312],{"class":262},[252,8873,1471],{"class":262},[252,8875,8876],{"class":900},"isiNdebele",[252,8878,3062],{"class":262},[252,8880,8881],{"class":254,"line":3002},[252,8882,1602],{"class":262},[252,8884,8885],{"class":254,"line":3007},[252,8886,7968],{"class":262},[252,8888,8889,8891,8893,8895,8897,8899,8902,8904],{"class":254,"line":3012},[252,8890,7973],{"class":262},[252,8892,249],{"class":1782},[252,8894,1266],{"class":262},[252,8896,312],{"class":262},[252,8898,1471],{"class":262},[252,8900,8901],{"class":900},"st",[252,8903,1266],{"class":262},[252,8905,323],{"class":262},[252,8907,8908,8910,8912,8914,8916,8918,8921,8923],{"class":254,"line":3805},[252,8909,7973],{"class":262},[252,8911,3536],{"class":1782},[252,8913,1266],{"class":262},[252,8915,312],{"class":262},[252,8917,1471],{"class":262},[252,8919,8920],{"class":900},"Southern Sotho",[252,8922,1266],{"class":262},[252,8924,323],{"class":262},[252,8926,8927,8929,8931,8933,8935,8937,8940],{"class":254,"line":3810},[252,8928,7973],{"class":262},[252,8930,8014],{"class":1782},[252,8932,1266],{"class":262},[252,8934,312],{"class":262},[252,8936,1471],{"class":262},[252,8938,8939],{"class":900},"Sesotho",[252,8941,3062],{"class":262},[252,8943,8944],{"class":254,"line":3822},[252,8945,1602],{"class":262},[252,8947,8948],{"class":254,"line":3832},[252,8949,7968],{"class":262},[252,8951,8952,8954,8956,8958,8960,8962,8965,8967],{"class":254,"line":3840},[252,8953,7973],{"class":262},[252,8955,249],{"class":1782},[252,8957,1266],{"class":262},[252,8959,312],{"class":262},[252,8961,1471],{"class":262},[252,8963,8964],{"class":900},"ss",[252,8966,1266],{"class":262},[252,8968,323],{"class":262},[252,8970,8971,8973,8975,8977,8979,8981,8984,8986],{"class":254,"line":3851},[252,8972,7973],{"class":262},[252,8974,3536],{"class":1782},[252,8976,1266],{"class":262},[252,8978,312],{"class":262},[252,8980,1471],{"class":262},[252,8982,8983],{"class":900},"Swati",[252,8985,1266],{"class":262},[252,8987,323],{"class":262},[252,8989,8990,8992,8994,8996,8998,9000,9003],{"class":254,"line":3858},[252,8991,7973],{"class":262},[252,8993,8014],{"class":1782},[252,8995,1266],{"class":262},[252,8997,312],{"class":262},[252,8999,1471],{"class":262},[252,9001,9002],{"class":900},"SiSwati",[252,9004,3062],{"class":262},[252,9006,9007],{"class":254,"line":3874},[252,9008,1602],{"class":262},[252,9010,9011],{"class":254,"line":3884},[252,9012,7968],{"class":262},[252,9014,9015,9017,9019,9021,9023,9025,9028,9030],{"class":254,"line":3889},[252,9016,7973],{"class":262},[252,9018,249],{"class":1782},[252,9020,1266],{"class":262},[252,9022,312],{"class":262},[252,9024,1471],{"class":262},[252,9026,9027],{"class":900},"tn",[252,9029,1266],{"class":262},[252,9031,323],{"class":262},[252,9033,9034,9036,9038,9040,9042,9044,9047,9049],{"class":254,"line":3897},[252,9035,7973],{"class":262},[252,9037,3536],{"class":1782},[252,9039,1266],{"class":262},[252,9041,312],{"class":262},[252,9043,1471],{"class":262},[252,9045,9046],{"class":900},"Tswana",[252,9048,1266],{"class":262},[252,9050,323],{"class":262},[252,9052,9053,9055,9057,9059,9061,9063,9066],{"class":254,"line":3907},[252,9054,7973],{"class":262},[252,9056,8014],{"class":1782},[252,9058,1266],{"class":262},[252,9060,312],{"class":262},[252,9062,1471],{"class":262},[252,9064,9065],{"class":900},"Setswana",[252,9067,3062],{"class":262},[252,9069,9070],{"class":254,"line":3916},[252,9071,1602],{"class":262},[252,9073,9074],{"class":254,"line":3924},[252,9075,7968],{"class":262},[252,9077,9078,9080,9082,9084,9086,9088,9090,9092],{"class":254,"line":3935},[252,9079,7973],{"class":262},[252,9081,249],{"class":1782},[252,9083,1266],{"class":262},[252,9085,312],{"class":262},[252,9087,1471],{"class":262},[252,9089,1930],{"class":900},[252,9091,1266],{"class":262},[252,9093,323],{"class":262},[252,9095,9096,9098,9100,9102,9104,9106,9109,9111],{"class":254,"line":3946},[252,9097,7973],{"class":262},[252,9099,3536],{"class":1782},[252,9101,1266],{"class":262},[252,9103,312],{"class":262},[252,9105,1471],{"class":262},[252,9107,9108],{"class":900},"Tsonga",[252,9110,1266],{"class":262},[252,9112,323],{"class":262},[252,9114,9115,9117,9119,9121,9123,9125,9128],{"class":254,"line":3953},[252,9116,7973],{"class":262},[252,9118,8014],{"class":1782},[252,9120,1266],{"class":262},[252,9122,312],{"class":262},[252,9124,1471],{"class":262},[252,9126,9127],{"class":900},"Xitsonga",[252,9129,3062],{"class":262},[252,9131,9132],{"class":254,"line":3964},[252,9133,1602],{"class":262},[252,9135,9136],{"class":254,"line":3969},[252,9137,7968],{"class":262},[252,9139,9140,9142,9144,9146,9148,9150,9153,9155],{"class":254,"line":3980},[252,9141,7973],{"class":262},[252,9143,249],{"class":1782},[252,9145,1266],{"class":262},[252,9147,312],{"class":262},[252,9149,1471],{"class":262},[252,9151,9152],{"class":900},"ve",[252,9154,1266],{"class":262},[252,9156,323],{"class":262},[252,9158,9159,9161,9163,9165,9167,9169,9172,9174],{"class":254,"line":3989},[252,9160,7973],{"class":262},[252,9162,3536],{"class":1782},[252,9164,1266],{"class":262},[252,9166,312],{"class":262},[252,9168,1471],{"class":262},[252,9170,9171],{"class":900},"Venda",[252,9173,1266],{"class":262},[252,9175,323],{"class":262},[252,9177,9178,9180,9182,9184,9186,9188,9191,9194,9196],{"class":254,"line":3994},[252,9179,7973],{"class":262},[252,9181,8014],{"class":1782},[252,9183,1266],{"class":262},[252,9185,312],{"class":262},[252,9187,1471],{"class":262},[252,9189,9190],{"class":900},"Tshiven",[252,9192,9193],{"class":266},"\\u1e13",[252,9195,58],{"class":900},[252,9197,3062],{"class":262},[252,9199,9200],{"class":254,"line":4006},[252,9201,1602],{"class":262},[252,9203,9204],{"class":254,"line":4016},[252,9205,7968],{"class":262},[252,9207,9208,9210,9212,9214,9216,9218,9221,9223],{"class":254,"line":4021},[252,9209,7973],{"class":262},[252,9211,249],{"class":1782},[252,9213,1266],{"class":262},[252,9215,312],{"class":262},[252,9217,1471],{"class":262},[252,9219,9220],{"class":900},"xh",[252,9222,1266],{"class":262},[252,9224,323],{"class":262},[252,9226,9227,9229,9231,9233,9235,9237,9240,9242],{"class":254,"line":4033},[252,9228,7973],{"class":262},[252,9230,3536],{"class":1782},[252,9232,1266],{"class":262},[252,9234,312],{"class":262},[252,9236,1471],{"class":262},[252,9238,9239],{"class":900},"Xhosa",[252,9241,1266],{"class":262},[252,9243,323],{"class":262},[252,9245,9246,9248,9250,9252,9254,9256,9259],{"class":254,"line":4040},[252,9247,7973],{"class":262},[252,9249,8014],{"class":1782},[252,9251,1266],{"class":262},[252,9253,312],{"class":262},[252,9255,1471],{"class":262},[252,9257,9258],{"class":900},"isiXhosa",[252,9260,3062],{"class":262},[252,9262,9263],{"class":254,"line":4050},[252,9264,1602],{"class":262},[252,9266,9267],{"class":254,"line":4061},[252,9268,7968],{"class":262},[252,9270,9271,9273,9275,9277,9279,9281,9284,9286],{"class":254,"line":4076},[252,9272,7973],{"class":262},[252,9274,249],{"class":1782},[252,9276,1266],{"class":262},[252,9278,312],{"class":262},[252,9280,1471],{"class":262},[252,9282,9283],{"class":900},"zu",[252,9285,1266],{"class":262},[252,9287,323],{"class":262},[252,9289,9290,9292,9294,9296,9298,9300,9303,9305],{"class":254,"line":4088},[252,9291,7973],{"class":262},[252,9293,3536],{"class":1782},[252,9295,1266],{"class":262},[252,9297,312],{"class":262},[252,9299,1471],{"class":262},[252,9301,9302],{"class":900},"Zulu",[252,9304,1266],{"class":262},[252,9306,323],{"class":262},[252,9308,9309,9311,9313,9315,9317,9319,9322],{"class":254,"line":4100},[252,9310,7973],{"class":262},[252,9312,8014],{"class":1782},[252,9314,1266],{"class":262},[252,9316,312],{"class":262},[252,9318,1471],{"class":262},[252,9320,9321],{"class":900},"isiZulu",[252,9323,3062],{"class":262},[252,9325,9326],{"class":254,"line":4107},[252,9327,8030],{"class":262},[252,9329,9331],{"class":254,"line":9330},68,[252,9332,8035],{"class":262},[252,9334,9336,9338,9340,9342,9344,9346,9349,9351],{"class":254,"line":9335},69,[252,9337,1495],{"class":262},[252,9339,8042],{"class":896},[252,9341,1266],{"class":262},[252,9343,312],{"class":262},[252,9345,1471],{"class":262},[252,9347,9348],{"class":900},"http:\u002F\u002Fassets.ipstack.com\u002Fflags\u002Fza.svg",[252,9350,1266],{"class":262},[252,9352,323],{"class":262},[252,9354,9356,9358,9360,9362,9364,9366,9369,9371],{"class":254,"line":9355},70,[252,9357,1495],{"class":262},[252,9359,8062],{"class":896},[252,9361,1266],{"class":262},[252,9363,312],{"class":262},[252,9365,1471],{"class":262},[252,9367,9368],{"class":266},"\\ud83c\\uddff\\ud83c\\udde6",[252,9370,1266],{"class":262},[252,9372,323],{"class":262},[252,9374,9376,9378,9380,9382,9384,9386,9389,9391],{"class":254,"line":9375},71,[252,9377,1495],{"class":262},[252,9379,8082],{"class":896},[252,9381,1266],{"class":262},[252,9383,312],{"class":262},[252,9385,1471],{"class":262},[252,9387,9388],{"class":900},"U+1F1FF U+1F1E6",[252,9390,1266],{"class":262},[252,9392,323],{"class":262},[252,9394,9396,9398,9400,9402,9404,9406,9409,9411],{"class":254,"line":9395},72,[252,9397,1495],{"class":262},[252,9399,8102],{"class":896},[252,9401,1266],{"class":262},[252,9403,312],{"class":262},[252,9405,1471],{"class":262},[252,9407,9408],{"class":900},"27",[252,9410,1266],{"class":262},[252,9412,323],{"class":262},[252,9414,9416,9418,9420,9422,9424],{"class":254,"line":9415},73,[252,9417,1495],{"class":262},[252,9419,8122],{"class":896},[252,9421,1266],{"class":262},[252,9423,312],{"class":262},[252,9425,9426],{"class":262}," false\n",[252,9428,9430],{"class":254,"line":9429},74,[252,9431,424],{"class":262},[252,9433,9435,9437,9439,9441,9443],{"class":254,"line":9434},75,[252,9436,3056],{"class":262},[252,9438,8139],{"class":258},[252,9440,1266],{"class":262},[252,9442,312],{"class":262},[252,9444,351],{"class":262},[252,9446,9448,9450,9452,9454,9456,9458,9461,9463],{"class":254,"line":9447},76,[252,9449,1495],{"class":262},[252,9451,8152],{"class":896},[252,9453,1266],{"class":262},[252,9455,312],{"class":262},[252,9457,1471],{"class":262},[252,9459,9460],{"class":900},"Africa\u002FJohannesburg",[252,9462,1266],{"class":262},[252,9464,323],{"class":262},[252,9466,9468,9470,9472,9474,9476,9478,9481,9483],{"class":254,"line":9467},77,[252,9469,1495],{"class":262},[252,9471,8172],{"class":896},[252,9473,1266],{"class":262},[252,9475,312],{"class":262},[252,9477,1471],{"class":262},[252,9479,9480],{"class":900},"2020-05-02T15:20:17+02:00",[252,9482,1266],{"class":262},[252,9484,323],{"class":262},[252,9486,9488,9490,9492,9494,9496,9498],{"class":254,"line":9487},78,[252,9489,1495],{"class":262},[252,9491,8192],{"class":896},[252,9493,1266],{"class":262},[252,9495,312],{"class":262},[252,9497,8199],{"class":1782},[252,9499,323],{"class":262},[252,9501,9503,9505,9507,9509,9511,9513,9516,9518],{"class":254,"line":9502},79,[252,9504,1495],{"class":262},[252,9506,249],{"class":896},[252,9508,1266],{"class":262},[252,9510,312],{"class":262},[252,9512,1471],{"class":262},[252,9514,9515],{"class":900},"SAST",[252,9517,1266],{"class":262},[252,9519,323],{"class":262},[252,9521,9523,9525,9527,9529,9531],{"class":254,"line":9522},80,[252,9524,1495],{"class":262},[252,9526,8227],{"class":896},[252,9528,1266],{"class":262},[252,9530,312],{"class":262},[252,9532,9426],{"class":262},[252,9534,9536],{"class":254,"line":9535},81,[252,9537,424],{"class":262},[252,9539,9541,9543,9545,9547,9549],{"class":254,"line":9540},82,[252,9542,3056],{"class":262},[252,9544,8244],{"class":258},[252,9546,1266],{"class":262},[252,9548,312],{"class":262},[252,9550,351],{"class":262},[252,9552,9554,9556,9558,9560,9562,9564,9567,9569],{"class":254,"line":9553},83,[252,9555,1495],{"class":262},[252,9557,249],{"class":896},[252,9559,1266],{"class":262},[252,9561,312],{"class":262},[252,9563,1471],{"class":262},[252,9565,9566],{"class":900},"ZAR",[252,9568,1266],{"class":262},[252,9570,323],{"class":262},[252,9572,9574,9576,9578,9580,9582,9584,9587,9589],{"class":254,"line":9573},84,[252,9575,1495],{"class":262},[252,9577,3536],{"class":896},[252,9579,1266],{"class":262},[252,9581,312],{"class":262},[252,9583,1471],{"class":262},[252,9585,9586],{"class":900},"South African Rand",[252,9588,1266],{"class":262},[252,9590,323],{"class":262},[252,9592,9594,9596,9598,9600,9602,9604,9607,9609],{"class":254,"line":9593},85,[252,9595,1495],{"class":262},[252,9597,8295],{"class":896},[252,9599,1266],{"class":262},[252,9601,312],{"class":262},[252,9603,1471],{"class":262},[252,9605,9606],{"class":900},"South African rand",[252,9608,1266],{"class":262},[252,9610,323],{"class":262},[252,9612,9614,9616,9618,9620,9622,9624,9626,9628],{"class":254,"line":9613},86,[252,9615,1495],{"class":262},[252,9617,8315],{"class":896},[252,9619,1266],{"class":262},[252,9621,312],{"class":262},[252,9623,1471],{"class":262},[252,9625,7011],{"class":900},[252,9627,1266],{"class":262},[252,9629,323],{"class":262},[252,9631,9633,9635,9637,9639,9641,9643,9645],{"class":254,"line":9632},87,[252,9634,1495],{"class":262},[252,9636,8335],{"class":896},[252,9638,1266],{"class":262},[252,9640,312],{"class":262},[252,9642,1471],{"class":262},[252,9644,7011],{"class":900},[252,9646,3062],{"class":262},[252,9648,9650],{"class":254,"line":9649},88,[252,9651,424],{"class":262},[252,9653,9655,9657,9659,9661,9663],{"class":254,"line":9654},89,[252,9656,3056],{"class":262},[252,9658,8356],{"class":258},[252,9660,1266],{"class":262},[252,9662,312],{"class":262},[252,9664,351],{"class":262},[252,9666,9668,9670,9672,9674,9676,9679],{"class":254,"line":9667},90,[252,9669,1495],{"class":262},[252,9671,8369],{"class":896},[252,9673,1266],{"class":262},[252,9675,312],{"class":262},[252,9677,9678],{"class":1782}," 61317",[252,9680,323],{"class":262},[252,9682,9684,9686,9688,9690,9692,9694,9697],{"class":254,"line":9683},91,[252,9685,1495],{"class":262},[252,9687,8385],{"class":896},[252,9689,1266],{"class":262},[252,9691,312],{"class":262},[252,9693,1471],{"class":262},[252,9695,9696],{"class":900},"Digital Energy Technologies Ltd.",[252,9698,3062],{"class":262},[252,9700,9702],{"class":254,"line":9701},92,[252,9703,8401],{"class":262},[252,9705,9707],{"class":254,"line":9706},93,[252,9708,8406],{"class":262},[26,9710,9711,9712,9717,9718,9721],{},"This looks like a dead end for now, let's check the domain. For that I use ",[58,9713,9716],{"href":9714,"rel":9715},"http:\u002F\u002Fdomaintools.com",[62],"DomainTools",", they have a pretty nice and free service for Whois lookup. So when I searched ",[249,9719,9720],{},"bocah.team"," I didn't get much, it looks like the domain has privacy protection, this means we cannot see who bought it.",[26,9723,9724,9725,9730,9731,9736,9737,9742,9743,9746,9747,9750,9751,9756,9757,9760],{},"But we can see a lot of other stuff, like it's registered by ",[58,9726,9729],{"href":9727,"rel":9728},"https:\u002F\u002Fwww.namecheap.com",[62],"NameCheap"," and protected by ",[58,9732,9735],{"href":9733,"rel":9734},"http:\u002F\u002Fwww.whoisguard.com\u002F",[62],"WhoisGuard",". It was created on 2018-07-13. The server the domain points to is a ",[58,9738,9741],{"href":9739,"rel":9740},"https:\u002F\u002Fwww.digitalocean.com",[62],"DigitalOcean"," one in the United Kingdom and his IP is ",[249,9744,9745],{},"188.166.152.221",". They got ",[249,9748,9749],{},"200"," status code back from an ",[58,9752,9755],{"href":9753,"rel":9754},"https:\u002F\u002Fwww.apache.org",[62],"Apache Web Server",", and it tells us there are ",[951,9758,9759],{},"257 other sites hosted on this server","! So it's time to do a reverse IP lookup for this server IP.",[26,9762,9763],{},[41,9764],{"alt":9765,"className":9766,"src":9767,"height":9768,"width":9769},"DomainTools Whois Report",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fdomain-tools-whois-report.webp",785,767,[26,9771,9772],{},"DomainTools don't let you do a reverse IP lookup for free, they'll show you couple of results and if you want to see more you need to buy a membership. We're amateurs here, I don't intend to throw away $99 per month for one reverse IP lookup, let's find another service.",[26,9774,9775,9776,105,9781,9786,9787,9790],{},"Actually there are quite a few that does that for free, like ",[58,9777,9780],{"href":9778,"rel":9779},"https:\u002F\u002Fhackertarget.com",[62],"Hacker Target",[58,9782,9785],{"href":9783,"rel":9784},"https:\u002F\u002Fviewdns.info\u002F",[62],"ViewDNS",". I especially love ViewDNS because it gives you the dates of ",[951,9788,9789],{},"Last Resolved"," for every domain, and gives you a total number at the top (so you can match its findings with other services).",[26,9792,9793],{},"ViewDNS found 242 domains, almost everything that DomainTools said there are, and it's quite the list.",[9795,9796,9797,9798,9802],"details",{},"\n  ",[9799,9800,9801],"summary",{},"Click here to see the 242 domains table.",[7498,9803,9804,9814],{},[7501,9805,9806],{},[7504,9807,9808,9811],{},[7507,9809,9810],{},"Domain",[7507,9812,9813],{},"Last Resolved Date",[7526,9815,9816,9824,9832,9840,9847,9854,9862,9869,9876,9883,9890,9897,9904,9911,9919,9926,9933,9940,9947,9954,9961,9969,9977,9984,9991,9998,10005,10012,10019,10026,10033,10040,10047,10054,10061,10069,10076,10083,10090,10097,10104,10111,10118,10126,10133,10141,10148,10155,10163,10170,10178,10185,10192,10199,10206,10213,10221,10228,10236,10243,10251,10259,10266,10273,10281,10289,10296,10303,10310,10317,10324,10331,10338,10345,10352,10359,10366,10373,10381,10388,10395,10402,10409,10416,10423,10430,10437,10444,10451,10458,10465,10472,10479,10486,10494,10501,10508,10516,10523,10530,10537,10545,10552,10559,10566,10573,10580,10587,10594,10601,10608,10615,10622,10629,10636,10644,10651,10658,10665,10672,10679,10686,10693,10700,10707,10714,10721,10728,10735,10742,10749,10756,10763,10770,10777,10784,10791,10798,10805,10812,10819,10826,10833,10840,10847,10854,10861,10868,10875,10882,10890,10897,10904,10911,10919,10926,10933,10940,10947,10954,10961,10968,10975,10982,10989,10996,11004,11011,11018,11025,11032,11039,11046,11054,11061,11068,11075,11082,11089,11096,11103,11110,11117,11124,11131,11138,11145,11152,11160,11167,11174,11181,11188,11195,11202,11209,11216,11223,11230,11237,11244,11252,11260,11267,11274,11281,11288,11295,11302,11309,11316,11323,11330,11337,11344,11352,11359,11366,11374,11381,11388,11395,11402,11409,11416,11423,11430,11437,11444,11451,11458,11465,11472,11479,11486,11493,11500,11507,11514,11521,11528,11536],{},[7504,9817,9818,9821],{},[7531,9819,9820],{},"1st-euro.net",[7531,9822,9823],{},"2020-04-27",[7504,9825,9826,9829],{},[7531,9827,9828],{},"2hard4you.com",[7531,9830,9831],{},"2020-04-29",[7504,9833,9834,9837],{},[7531,9835,9836],{},"32399753810sb.com",[7531,9838,9839],{},"2019-10-29",[7504,9841,9842,9845],{},[7531,9843,9844],{},"39832302670sb.com",[7531,9846,9839],{},[7504,9848,9849,9852],{},[7531,9850,9851],{},"404fizzy.com",[7531,9853,9831],{},[7504,9855,9856,9859],{},[7531,9857,9858],{},"53nn0r4x.org",[7531,9860,9861],{},"2020-05-02",[7504,9863,9864,9867],{},[7531,9865,9866],{},"ablegod.net",[7531,9868,9823],{},[7504,9870,9871,9874],{},[7531,9872,9873],{},"adobe-results.net",[7531,9875,9823],{},[7504,9877,9878,9881],{},[7531,9879,9880],{},"adobepdf.org",[7531,9882,9861],{},[7504,9884,9885,9888],{},[7531,9886,9887],{},"adobexx.com",[7531,9889,9831],{},[7504,9891,9892,9895],{},[7531,9893,9894],{},"akangs.com",[7531,9896,9831],{},[7504,9898,9899,9902],{},[7531,9900,9901],{},"alli7f9651a12362cec298fe16c1574dcead.com",[7531,9903,9839],{},[7504,9905,9906,9909],{},[7531,9907,9908],{},"amaslim.com",[7531,9910,9831],{},[7504,9912,9913,9916],{},[7531,9914,9915],{},"ap13rc3.com",[7531,9917,9918],{},"2019-10-03",[7504,9920,9921,9924],{},[7531,9922,9923],{},"apelngentots.com",[7531,9925,9831],{},[7504,9927,9928,9931],{},[7531,9929,9930],{},"aplasitecomcom.com",[7531,9932,9831],{},[7504,9934,9935,9938],{},[7531,9936,9937],{},"approject.net",[7531,9939,9823],{},[7504,9941,9942,9945],{},[7531,9943,9944],{},"armcokeresulter.com",[7531,9946,9831],{},[7504,9948,9949,9952],{},[7531,9950,9951],{},"asdhfkjds.com",[7531,9953,9831],{},[7504,9955,9956,9959],{},[7531,9957,9958],{},"asdsadsa.com",[7531,9960,9831],{},[7504,9962,9963,9966],{},[7531,9964,9965],{},"astsmtp.org",[7531,9967,9968],{},"2019-10-11",[7504,9970,9971,9974],{},[7531,9972,9973],{},"balusaz.com",[7531,9975,9976],{},"2020-03-29",[7504,9978,9979,9982],{},[7531,9980,9981],{},"banglemail.com",[7531,9983,9831],{},[7504,9985,9986,9989],{},[7531,9987,9988],{},"baonlineintl.com",[7531,9990,9831],{},[7504,9992,9993,9996],{},[7531,9994,9995],{},"bdcimail.com",[7531,9997,9831],{},[7504,9999,10000,10003],{},[7531,10001,10002],{},"blckman.com",[7531,10004,9831],{},[7504,10006,10007,10010],{},[7531,10008,10009],{},"blowjoob.com",[7531,10011,9831],{},[7504,10013,10014,10017],{},[7531,10015,10016],{},"blurdybloop.com",[7531,10018,9831],{},[7504,10020,10021,10024],{},[7531,10022,10023],{},"boarsw1.com",[7531,10025,9831],{},[7504,10027,10028,10031],{},[7531,10029,10030],{},"boarsw11.com",[7531,10032,9831],{},[7504,10034,10035,10038],{},[7531,10036,10037],{},"boarsw2.com",[7531,10039,9831],{},[7504,10041,10042,10044],{},[7531,10043,9720],{},[7531,10045,10046],{},"2020-05-03",[7504,10048,10049,10052],{},[7531,10050,10051],{},"btemailsupport.com",[7531,10053,9831],{},[7504,10055,10056,10059],{},[7531,10057,10058],{},"c0x1.com",[7531,10060,9831],{},[7504,10062,10063,10066],{},[7531,10064,10065],{},"c1onet.com",[7531,10067,10068],{},"2019-10-19",[7504,10070,10071,10074],{},[7531,10072,10073],{},"cc-result.com",[7531,10075,9831],{},[7504,10077,10078,10081],{},[7531,10079,10080],{},"ch3rsw2.com",[7531,10082,9831],{},[7504,10084,10085,10088],{},[7531,10086,10087],{},"charles-robinson.com",[7531,10089,9831],{},[7504,10091,10092,10095],{},[7531,10093,10094],{},"chasemelom.com",[7531,10096,9831],{},[7504,10098,10099,10102],{},[7531,10100,10101],{},"chsw11.com",[7531,10103,9831],{},[7504,10105,10106,10109],{},[7531,10107,10108],{},"chsww4.com",[7531,10110,9831],{},[7504,10112,10113,10116],{},[7531,10114,10115],{},"cielo2012.com",[7531,10117,9831],{},[7504,10119,10120,10123],{},[7531,10121,10122],{},"clientcaf.info",[7531,10124,10125],{},"2020-04-28",[7504,10127,10128,10131],{},[7531,10129,10130],{},"cnutzdtrk.com",[7531,10132,9831],{},[7504,10134,10135,10138],{},[7531,10136,10137],{},"coin-c.pro",[7531,10139,10140],{},"2020-01-30",[7504,10142,10143,10146],{},[7531,10144,10145],{},"ctrhackschool.com",[7531,10147,9831],{},[7504,10149,10150,10153],{},[7531,10151,10152],{},"d1berkati.net",[7531,10154,10046],{},[7504,10156,10157,10160],{},[7531,10158,10159],{},"d3sxsrw.com",[7531,10161,10162],{},"2019-11-12",[7504,10164,10165,10168],{},[7531,10166,10167],{},"darego.org",[7531,10169,9861],{},[7504,10171,10172,10175],{},[7531,10173,10174],{},"dav1nc1.com",[7531,10176,10177],{},"2019-08-07",[7504,10179,10180,10183],{},[7531,10181,10182],{},"denate-djf88.com",[7531,10184,9831],{},[7504,10186,10187,10190],{},[7531,10188,10189],{},"devexoxfamjackpot.org",[7531,10191,10177],{},[7504,10193,10194,10197],{},[7531,10195,10196],{},"docusign1.com",[7531,10198,9831],{},[7504,10200,10201,10204],{},[7531,10202,10203],{},"donking.net",[7531,10205,9823],{},[7504,10207,10208,10211],{},[7531,10209,10210],{},"dpvmx.com",[7531,10212,9831],{},[7504,10214,10215,10218],{},[7531,10216,10217],{},"dullandstupidfolks.com",[7531,10219,10220],{},"2019-10-26",[7504,10222,10223,10226],{},[7531,10224,10225],{},"eaglesmail.net",[7531,10227,9823],{},[7504,10229,10230,10233],{},[7531,10231,10232],{},"eentertainment.site",[7531,10234,10235],{},"2019-09-13",[7504,10237,10238,10241],{},[7531,10239,10240],{},"esqhackschool.com",[7531,10242,9831],{},[7504,10244,10245,10248],{},[7531,10246,10247],{},"expertgz.com",[7531,10249,10250],{},"2020-03-22",[7504,10252,10253,10256],{},[7531,10254,10255],{},"familytreetmatter.com",[7531,10257,10258],{},"2019-11-13",[7504,10260,10261,10264],{},[7531,10262,10263],{},"fcwbanking.com",[7531,10265,9831],{},[7504,10267,10268,10271],{},[7531,10269,10270],{},"forcadesempre.com",[7531,10272,9831],{},[7504,10274,10275,10278],{},[7531,10276,10277],{},"fortiittechnology.com",[7531,10279,10280],{},"2019-08-21",[7504,10282,10283,10286],{},[7531,10284,10285],{},"freakzbrothers.team",[7531,10287,10288],{},"2019-10-25",[7504,10290,10291,10294],{},[7531,10292,10293],{},"frreer.com",[7531,10295,9831],{},[7504,10297,10298,10301],{},[7531,10299,10300],{},"garutc0de.com",[7531,10302,9831],{},[7504,10304,10305,10308],{},[7531,10306,10307],{},"gcrele.com",[7531,10309,9831],{},[7504,10311,10312,10315],{},[7531,10313,10314],{},"ggledocblessing.com",[7531,10316,9831],{},[7504,10318,10319,10322],{},[7531,10320,10321],{},"ggledocs.com",[7531,10323,9831],{},[7504,10325,10326,10329],{},[7531,10327,10328],{},"globizsolution.com",[7531,10330,10046],{},[7504,10332,10333,10336],{},[7531,10334,10335],{},"gmailphish.com",[7531,10337,9831],{},[7504,10339,10340,10343],{},[7531,10341,10342],{},"gmdddail.com",[7531,10344,9831],{},[7504,10346,10347,10350],{},[7531,10348,10349],{},"goldenboyplanet.net",[7531,10351,9823],{},[7504,10353,10354,10357],{},[7531,10355,10356],{},"goldenmarine.net",[7531,10358,9823],{},[7504,10360,10361,10364],{},[7531,10362,10363],{},"goodpostman.com",[7531,10365,9831],{},[7504,10367,10368,10371],{},[7531,10369,10370],{},"googdocs.org",[7531,10372,9861],{},[7504,10374,10375,10378],{},[7531,10376,10377],{},"groovypigthing.com",[7531,10379,10380],{},"2019-12-13",[7504,10382,10383,10386],{},[7531,10384,10385],{},"h4l1f4x.com",[7531,10387,9831],{},[7504,10389,10390,10393],{},[7531,10391,10392],{},"humaxgifts.net",[7531,10394,9823],{},[7504,10396,10397,10400],{},[7531,10398,10399],{},"hyzeek.com",[7531,10401,9831],{},[7504,10403,10404,10407],{},[7531,10405,10406],{},"idbte4m.com",[7531,10408,9831],{},[7504,10410,10411,10414],{},[7531,10412,10413],{},"idyat.com",[7531,10415,9831],{},[7504,10417,10418,10421],{},[7531,10419,10420],{},"iestoreakeup.com",[7531,10422,9831],{},[7504,10424,10425,10428],{},[7531,10426,10427],{},"ifastnet1.com",[7531,10429,9831],{},[7504,10431,10432,10435],{},[7531,10433,10434],{},"ilekunanu.com",[7531,10436,10046],{},[7504,10438,10439,10442],{},[7531,10440,10441],{},"iln-mc.net",[7531,10443,9823],{},[7504,10445,10446,10449],{},[7531,10447,10448],{},"immaculatelord.com",[7531,10450,9831],{},[7504,10452,10453,10456],{},[7531,10454,10455],{},"inforezult.com",[7531,10457,9831],{},[7504,10459,10460,10463],{},[7531,10461,10462],{},"infulz.org",[7531,10464,9861],{},[7504,10466,10467,10470],{},[7531,10468,10469],{},"inv3st3c.com",[7531,10471,9839],{},[7504,10473,10474,10477],{},[7531,10475,10476],{},"j4nn1ck.com",[7531,10478,9831],{},[7504,10480,10481,10484],{},[7531,10482,10483],{},"jephy-webmail.com",[7531,10485,9831],{},[7504,10487,10488,10491],{},[7531,10489,10490],{},"jquery-cloud.org",[7531,10492,10493],{},"2019-11-15",[7504,10495,10496,10499],{},[7531,10497,10498],{},"kanbghik.com",[7531,10500,9831],{},[7504,10502,10503,10506],{},[7531,10504,10505],{},"killdemall.com",[7531,10507,9831],{},[7504,10509,10510,10513],{},[7531,10511,10512],{},"kind1.org",[7531,10514,10515],{},"2019-09-26",[7504,10517,10518,10521],{},[7531,10519,10520],{},"kindly2014.com",[7531,10522,9831],{},[7504,10524,10525,10528],{},[7531,10526,10527],{},"kksdfs.com",[7531,10529,9831],{},[7504,10531,10532,10535],{},[7531,10533,10534],{},"kod3r.com",[7531,10536,9831],{},[7504,10538,10539,10542],{},[7531,10540,10541],{},"kucinghitam.team",[7531,10543,10544],{},"2019-07-12",[7504,10546,10547,10550],{},[7531,10548,10549],{},"kuhlcomputer.com",[7531,10551,9831],{},[7504,10553,10554,10557],{},[7531,10555,10556],{},"lagoshacker.com",[7531,10558,9831],{},[7504,10560,10561,10564],{},[7531,10562,10563],{},"lbox1.com",[7531,10565,9831],{},[7504,10567,10568,10571],{},[7531,10569,10570],{},"lbox2.com",[7531,10572,9831],{},[7504,10574,10575,10578],{},[7531,10576,10577],{},"lbox3.com",[7531,10579,9831],{},[7504,10581,10582,10585],{},[7531,10583,10584],{},"legitz-solutions.com",[7531,10586,9831],{},[7504,10588,10589,10592],{},[7531,10590,10591],{},"linksandmail.com",[7531,10593,9831],{},[7504,10595,10596,10599],{},[7531,10597,10598],{},"llgss.com",[7531,10600,9831],{},[7504,10602,10603,10606],{},[7531,10604,10605],{},"magentoscure.com",[7531,10607,9831],{},[7504,10609,10610,10613],{},[7531,10611,10612],{},"mailfahad.com",[7531,10614,9831],{},[7504,10616,10617,10620],{},[7531,10618,10619],{},"mailrez.com",[7531,10621,9831],{},[7504,10623,10624,10627],{},[7531,10625,10626],{},"malancellc.com",[7531,10628,9831],{},[7504,10630,10631,10634],{},[7531,10632,10633],{},"mdhmx.com",[7531,10635,9831],{},[7504,10637,10638,10641],{},[7531,10639,10640],{},"mefffdo.com",[7531,10642,10643],{},"2019-10-10",[7504,10645,10646,10649],{},[7531,10647,10648],{},"menemoney.com",[7531,10650,9831],{},[7504,10652,10653,10656],{},[7531,10654,10655],{},"mimecastphish.com",[7531,10657,9831],{},[7504,10659,10660,10663],{},[7531,10661,10662],{},"minioncc.com",[7531,10664,9831],{},[7504,10666,10667,10670],{},[7531,10668,10669],{},"minionlogin.com",[7531,10671,9831],{},[7504,10673,10674,10677],{},[7531,10675,10676],{},"minionresult.com",[7531,10678,9831],{},[7504,10680,10681,10684],{},[7531,10682,10683],{},"moneysquad.org",[7531,10685,9861],{},[7504,10687,10688,10691],{},[7531,10689,10690],{},"moxxxx.com",[7531,10692,9831],{},[7504,10694,10695,10698],{},[7531,10696,10697],{},"mrspybotv3.com",[7531,10699,9831],{},[7504,10701,10702,10705],{},[7531,10703,10704],{},"mrspybotv4.com",[7531,10706,9831],{},[7504,10708,10709,10712],{},[7531,10710,10711],{},"mteen.net",[7531,10713,9823],{},[7504,10715,10716,10719],{},[7531,10717,10718],{},"mylinklog.com",[7531,10720,9831],{},[7504,10722,10723,10726],{},[7531,10724,10725],{},"mymailgin.com",[7531,10727,9831],{},[7504,10729,10730,10733],{},[7531,10731,10732],{},"mystshop.org",[7531,10734,9861],{},[7504,10736,10737,10740],{},[7531,10738,10739],{},"nationwidez.com",[7531,10741,9831],{},[7504,10743,10744,10747],{},[7531,10745,10746],{},"neids.net",[7531,10748,9823],{},[7504,10750,10751,10754],{},[7531,10752,10753],{},"netzrxflix.com",[7531,10755,9831],{},[7504,10757,10758,10761],{},[7531,10759,10760],{},"newbieking.biz",[7531,10762,9861],{},[7504,10764,10765,10768],{},[7531,10766,10767],{},"newcpanel.com",[7531,10769,9831],{},[7504,10771,10772,10775],{},[7531,10773,10774],{},"notforsal.org",[7531,10776,9861],{},[7504,10778,10779,10782],{},[7531,10780,10781],{},"notinforreal.org",[7531,10783,9861],{},[7504,10785,10786,10789],{},[7531,10787,10788],{},"oluxshopservice.com",[7531,10790,10162],{},[7504,10792,10793,10796],{},[7531,10794,10795],{},"one-sender.com",[7531,10797,9831],{},[7504,10799,10800,10803],{},[7531,10801,10802],{},"oneskilet.team",[7531,10804,10250],{},[7504,10806,10807,10810],{},[7531,10808,10809],{},"oonlo.com",[7531,10811,9831],{},[7504,10813,10814,10817],{},[7531,10815,10816],{},"orimi.co",[7531,10818,9861],{},[7504,10820,10821,10824],{},[7531,10822,10823],{},"otherphish.com",[7531,10825,9831],{},[7504,10827,10828,10831],{},[7531,10829,10830],{},"ourtimesupport.com",[7531,10832,9831],{},[7504,10834,10835,10838],{},[7531,10836,10837],{},"ourtimewhorers.com",[7531,10839,10046],{},[7504,10841,10842,10845],{},[7531,10843,10844],{},"outlookphish.com",[7531,10846,9831],{},[7504,10848,10849,10852],{},[7531,10850,10851],{},"p-delivr.com",[7531,10853,9831],{},[7504,10855,10856,10859],{},[7531,10857,10858],{},"pant8.com",[7531,10860,9831],{},[7504,10862,10863,10866],{},[7531,10864,10865],{},"perpeleran.com",[7531,10867,9831],{},[7504,10869,10870,10873],{},[7531,10871,10872],{},"pollarda.com",[7531,10874,9831],{},[7504,10876,10877,10880],{},[7531,10878,10879],{},"ppaypl.com",[7531,10881,9831],{},[7504,10883,10884,10887],{},[7531,10885,10886],{},"priv8scam.com",[7531,10888,10889],{},"2020-02-09",[7504,10891,10892,10895],{},[7531,10893,10894],{},"priv8scamccforu.com",[7531,10896,10889],{},[7504,10898,10899,10902],{},[7531,10900,10901],{},"private-relay.com",[7531,10903,9831],{},[7504,10905,10906,10909],{},[7531,10907,10908],{},"projectmy.net",[7531,10910,9823],{},[7504,10912,10913,10916],{},[7531,10914,10915],{},"pvscamccsscom.com",[7531,10917,10918],{},"2019-09-28",[7504,10920,10921,10924],{},[7531,10922,10923],{},"pvscamyasscom.com",[7531,10925,9831],{},[7504,10927,10928,10931],{},[7531,10929,10930],{},"pvscamyavscom.com",[7531,10932,10918],{},[7504,10934,10935,10938],{},[7531,10936,10937],{},"qqrez.com",[7531,10939,9831],{},[7504,10941,10942,10945],{},[7531,10943,10944],{},"r36yc.com",[7531,10946,9831],{},[7504,10948,10949,10952],{},[7531,10950,10951],{},"raflipedia.com",[7531,10953,9831],{},[7504,10955,10956,10959],{},[7531,10957,10958],{},"recodz.com",[7531,10960,9831],{},[7504,10962,10963,10966],{},[7531,10964,10965],{},"resulttidaklancar.com",[7531,10967,9831],{},[7504,10969,10970,10973],{},[7531,10971,10972],{},"rezlt.org",[7531,10974,9861],{},[7504,10976,10977,10980],{},[7531,10978,10979],{},"rezltboa.com",[7531,10981,9831],{},[7504,10983,10984,10987],{},[7531,10985,10986],{},"rezult.org",[7531,10988,10046],{},[7504,10990,10991,10994],{},[7531,10992,10993],{},"rezultbossing.com",[7531,10995,9831],{},[7504,10997,10998,11001],{},[7531,10999,11000],{},"rsjkingdomxpp.pro",[7531,11002,11003],{},"2019-06-11",[7504,11005,11006,11009],{},[7531,11007,11008],{},"salkah.com",[7531,11010,9831],{},[7504,11012,11013,11016],{},[7531,11014,11015],{},"saydie.com",[7531,11017,9831],{},[7504,11019,11020,11023],{},[7531,11021,11022],{},"se-holldings.com",[7531,11024,9831],{},[7504,11026,11027,11030],{},[7531,11028,11029],{},"sendmsexcel.com",[7531,11031,9831],{},[7504,11033,11034,11037],{},[7531,11035,11036],{},"serverstrato.net",[7531,11038,9823],{},[7504,11040,11041,11044],{},[7531,11042,11043],{},"serviceadobe.com",[7531,11045,9831],{},[7504,11047,11048,11051],{},[7531,11049,11050],{},"servicedropbox.com",[7531,11052,11053],{},"2019-08-24",[7504,11055,11056,11059],{},[7531,11057,11058],{},"servicesoutlook.com",[7531,11060,9831],{},[7504,11062,11063,11066],{},[7531,11064,11065],{},"servisdocusign.com",[7531,11067,9831],{},[7504,11069,11070,11073],{},[7531,11071,11072],{},"servisdropbox.com",[7531,11074,9831],{},[7504,11076,11077,11080],{},[7531,11078,11079],{},"sidshell.com",[7531,11081,10250],{},[7504,11083,11084,11087],{},[7531,11085,11086],{},"skilet.team",[7531,11088,10250],{},[7504,11090,11091,11094],{},[7531,11092,11093],{},"slackerc0de.com",[7531,11095,9831],{},[7504,11097,11098,11101],{},[7531,11099,11100],{},"slclogin.com",[7531,11102,10046],{},[7504,11104,11105,11108],{},[7531,11106,11107],{},"sn7ak.com",[7531,11109,9831],{},[7504,11111,11112,11115],{},[7531,11113,11114],{},"soutaz.com",[7531,11116,9831],{},[7504,11118,11119,11122],{},[7531,11120,11121],{},"spammerindo.com",[7531,11123,9831],{},[7504,11125,11126,11129],{},[7531,11127,11128],{},"spammerindo.pro",[7531,11130,11003],{},[7504,11132,11133,11136],{},[7531,11134,11135],{},"spirititus.com",[7531,11137,9831],{},[7504,11139,11140,11143],{},[7531,11141,11142],{},"spmers.net",[7531,11144,9823],{},[7504,11146,11147,11150],{},[7531,11148,11149],{},"spyu.org",[7531,11151,9861],{},[7504,11153,11154,11157],{},[7531,11155,11156],{},"srv-app.club",[7531,11158,11159],{},"2019-07-02",[7504,11161,11162,11165],{},[7531,11163,11164],{},"stationlinux.org",[7531,11166,9861],{},[7504,11168,11169,11172],{},[7531,11170,11171],{},"support-itrueserver.xyz",[7531,11173,10250],{},[7504,11175,11176,11179],{},[7531,11177,11178],{},"sureboi.com",[7531,11180,9831],{},[7504,11182,11183,11186],{},[7531,11184,11185],{},"svoooo.com",[7531,11187,9831],{},[7504,11189,11190,11193],{},[7531,11191,11192],{},"sydoppe.com",[7531,11194,9831],{},[7504,11196,11197,11200],{},[7531,11198,11199],{},"synichix.pro",[7531,11201,11003],{},[7504,11203,11204,11207],{},[7531,11205,11206],{},"system42l.net",[7531,11208,9823],{},[7504,11210,11211,11214],{},[7531,11212,11213],{},"t3chsss.net",[7531,11215,9823],{},[7504,11217,11218,11221],{},[7531,11219,11220],{},"teeniecamp4free.com",[7531,11222,9831],{},[7504,11224,11225,11228],{},[7531,11226,11227],{},"teluz.org",[7531,11229,10643],{},[7504,11231,11232,11235],{},[7531,11233,11234],{},"tf-info.com",[7531,11236,9831],{},[7504,11238,11239,11242],{},[7531,11240,11241],{},"tgboi.com",[7531,11243,9831],{},[7504,11245,11246,11249],{},[7531,11247,11248],{},"tgiftoday.biz",[7531,11250,11251],{},"2019-08-20",[7504,11253,11254,11257],{},[7531,11255,11256],{},"thanksforreal.org",[7531,11258,11259],{},"2019-12-12",[7504,11261,11262,11265],{},[7531,11263,11264],{},"theaccessuk.org",[7531,11266,9861],{},[7504,11268,11269,11272],{},[7531,11270,11271],{},"tool4spam.com",[7531,11273,9831],{},[7504,11275,11276,11279],{},[7531,11277,11278],{},"tooolz.com",[7531,11280,9831],{},[7504,11282,11283,11286],{},[7531,11284,11285],{},"tooxlz-db.com",[7531,11287,9831],{},[7504,11289,11290,11293],{},[7531,11291,11292],{},"trowey.com",[7531,11294,9831],{},[7504,11296,11297,11300],{},[7531,11298,11299],{},"tsbdumbs.com",[7531,11301,9831],{},[7504,11303,11304,11307],{},[7531,11305,11306],{},"tt-door.biz",[7531,11308,9861],{},[7504,11310,11311,11314],{},[7531,11312,11313],{},"ttcpanel.com",[7531,11315,9831],{},[7504,11317,11318,11321],{},[7531,11319,11320],{},"twinbash.co",[7531,11322,9861],{},[7504,11324,11325,11328],{},[7531,11326,11327],{},"usaaresults.com",[7531,11329,9831],{},[7504,11331,11332,11335],{},[7531,11333,11334],{},"usaaxa.com",[7531,11336,10046],{},[7504,11338,11339,11342],{},[7531,11340,11341],{},"vgnnb.com",[7531,11343,9831],{},[7504,11345,11346,11349],{},[7531,11347,11348],{},"virus-ma.com",[7531,11350,11351],{},"2019-08-09",[7504,11353,11354,11357],{},[7531,11355,11356],{},"webmai.co",[7531,11358,9861],{},[7504,11360,11361,11364],{},[7531,11362,11363],{},"webmailupdate.com",[7531,11365,9831],{},[7504,11367,11368,11371],{},[7531,11369,11370],{},"willyfucker.com",[7531,11372,11373],{},"2019-11-27",[7504,11375,11376,11379],{},[7531,11377,11378],{},"windowsswebs.com",[7531,11380,9839],{},[7504,11382,11383,11386],{},[7531,11384,11385],{},"wiregang.com",[7531,11387,11351],{},[7504,11389,11390,11393],{},[7531,11391,11392],{},"wirethings.net",[7531,11394,10046],{},[7504,11396,11397,11400],{},[7531,11398,11399],{},"wls1.com",[7531,11401,9831],{},[7504,11403,11404,11407],{},[7531,11405,11406],{},"worldpc2000.com",[7531,11408,9831],{},[7504,11410,11411,11414],{},[7531,11412,11413],{},"xellef-id.com",[7531,11415,9831],{},[7504,11417,11418,11421],{},[7531,11419,11420],{},"xhades.com",[7531,11422,9831],{},[7504,11424,11425,11428],{},[7531,11426,11427],{},"xindex.org",[7531,11429,9861],{},[7504,11431,11432,11435],{},[7531,11433,11434],{},"xsendersecurity.com",[7531,11436,9831],{},[7504,11438,11439,11442],{},[7531,11440,11441],{},"xvbvx.com",[7531,11443,9831],{},[7504,11445,11446,11449],{},[7531,11447,11448],{},"xxsender.com",[7531,11450,9831],{},[7504,11452,11453,11456],{},[7531,11454,11455],{},"xxxsender.com",[7531,11457,9831],{},[7504,11459,11460,11463],{},[7531,11461,11462],{},"yah5oo.com",[7531,11464,9831],{},[7504,11466,11467,11470],{},[7531,11468,11469],{},"yandew.com",[7531,11471,9831],{},[7504,11473,11474,11477],{},[7531,11475,11476],{},"yatdew.com",[7531,11478,9831],{},[7504,11480,11481,11484],{},[7531,11482,11483],{},"youphei.com",[7531,11485,10380],{},[7504,11487,11488,11491],{},[7531,11489,11490],{},"yourzcoolsite.com",[7531,11492,11259],{},[7504,11494,11495,11498],{},[7531,11496,11497],{},"yyuuoo.com",[7531,11499,9831],{},[7504,11501,11502,11505],{},[7531,11503,11504],{},"z1t0ng404.com",[7531,11506,9831],{},[7504,11508,11509,11512],{},[7531,11510,11511],{},"z3ran.com",[7531,11513,9831],{},[7504,11515,11516,11519],{},[7531,11517,11518],{},"zebyinbox.com",[7531,11520,9831],{},[7504,11522,11523,11526],{},[7531,11524,11525],{},"zenquel.com",[7531,11527,9831],{},[7504,11529,11530,11533],{},[7531,11531,11532],{},"zephyrsc.shop",[7531,11534,11535],{},"2019-10-23",[7504,11537,11538,11541],{},[7531,11539,11540],{},"zwirgel.net",[7531,11542,9823],{},[26,11544,11545],{},"When we go over it, we can see there are a lot of them that are probably used to create websites for phishing attacks (domains that are close to other big service domains \u002F names), and others for spam and the like. So that gives us a lot of leads to check, we can Google them all and see what comes up, but I don't have the patience to go over results for 242 searches right now. So let's keep it for later, if we need to.",[26,11547,11548,11549,983],{},"Let's check the website itself. It looks like just a blank page. I did a quick check for the body \u002F headers that returned for the request (as well as for other requests that the web browser did) and I didn't see anything suspicious (like a header on the response that's not supposed to be there, I got there by thinking it'll probably be a ",[58,11550,11553],{"href":11551,"rel":11552},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCommand_and_control",[62],"C2",[26,11555,11556,11557,11560,11561,11566],{},"The next question that pops into my head is ",[951,11558,11559],{},"\"was it always a blank page?\"",". We have a way to check it, maybe, sort of. We'll use the ",[58,11562,11565],{"href":11563,"rel":11564},"https:\u002F\u002Fweb.archive.org\u002F",[62],"WayBack Machine"," and we got some results! We can see it saves 3 snapshots, but all of them are the same blank pages.",[26,11568,11569],{},[41,11570],{"alt":11565,"className":11571,"src":11572},[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fwayback-machine.webp",[26,11574,11575,11576,11578],{},"So this gives us nothing so far. What comes to mind is that it's someone renting a server on DigitalOcean and using proxies to map some parts of the internet and when he gets back ",[249,11577,9749],{}," from a website he tries to login, maybe it's automated login attempts maybe it isn't, I haven't decided yet (because it looks automated but then I was expecting an increase of a number or the next letter in the alphabet, or to add some letter at the end of the password in the second try, if it was automated). Also, it's probably someone (or a bunch of people) who's doing some phishing and \u002F or spam stuff.",[458,11580,11582],{"id":11581},"_3-like-a-dog-with-a-bone","3. Like a dog with a bone",[26,11584,11585,11586,11591],{},"This is not the end of the line, it's just the start. Let's keep digging! A tool I love to use is ",[58,11587,11590],{"href":11588,"rel":11589},"https:\u002F\u002Fsitereport.netcraft.com",[62],"Netcraft Site Report",", when I run the domain in their service we finally start to see more results.",[26,11593,11594],{},[41,11595],{"alt":11596,"className":11597,"src":11598},"Netcraft Report",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fnetcraft-report.webp",[26,11600,11601,11602,11605,11606,11611],{},"We can see the domain registered with ",[249,11603,11604],{},"donuts.co"," (which redirects us to ",[58,11607,11610],{"href":11608,"rel":11609},"https:\u002F\u002Fdonuts.domains",[62],"donuts.domains",") back in 2017! (maybe it was someone else, maybe they forgot to renew it and rebought it with NameCheap, or maybe they transferred it to NameCheap in 2018, who knows? don't really care at this point).",[26,11613,11614,11615,11618,11619,11622,11623,11628,11629,11632,11633,11635,11636,11639,11640,126],{},"The nice thing we find is the organization that purchased the domain back in 2017 (they didn't have a domain privacy protection). It's ",[249,11616,11617],{},"Jalanin aja dulu mhanxx, hax0r, 50701, United States",". First thing that pops up is the word ",[249,11620,11621],{},"hax0r",", this is ",[58,11624,11627],{"href":11625,"rel":11626},"https:\u002F\u002Fsimple.wikipedia.org\u002Fwiki\u002FLeet",[62],"1337 speak (or leet)"," and it is used on the internet in forums, chat rooms, online games, etc. The ",[249,11630,11631],{},"0"," in the word is equivalent to ",[249,11634,6089],{}," so it's the word ",[249,11637,11638],{},"haxor"," and it's an ",[58,11641,11644],{"href":11642,"rel":11643},"https:\u002F\u002Fen.wiktionary.org\u002Fwiki\u002Fhaxor",[62],"alternative spelling of hacker or script kiddie",[26,11646,11647],{},"So that's interesting, let's keep our focus on domain \u002F website \u002F server.",[26,11649,11650,11651,11656,11657,11660,11661,11665,11666,11671,11672,120,11674,983],{},"More scanning with Netcraft and ",[58,11652,11655],{"href":11653,"rel":11654},"https:\u002F\u002Fbuiltwith.com",[62],"build with"," finds it's probably a Linux server (maybe Ubuntu, based on ",[951,11658,11659],{},"\"build with\"","), and it uses ",[58,11662,11664],{"href":9753,"rel":11663},[62],"Apache",". It used Apache 2.4, for sometime it also had ",[58,11667,11670],{"href":11668,"rel":11669},"https:\u002F\u002Fwww.nginx.com\u002F",[62],"Nginx"," but now we only got \"Apache\". And this domain is active since the end of 2017 (based on ",[951,11673,11659],{},[951,11675,11676],{},"Netcraft",[26,11678,11679],{},[41,11680],{"alt":11681,"className":11682,"src":11683,"height":11684,"width":11685},"Build With",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fbuilt-with.webp",539,809,[26,11687,11688],{},[41,11689],{"alt":11690,"className":11691,"src":11692,"height":11693,"width":11694},"Netcarft Hosting",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fnetcraft-hosting.webp",162,1339,[26,11696,11697,11698,11703],{},"Another big finding from Netcraft is that it uses ",[58,11699,11702],{"href":11700,"rel":11701},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FDMARC",[62],"DMARC",". DMARC is an email authentication protocol, and Netcraft gives us the raw record which details the emails they use for reporting aggregate and failure data.",[242,11705,11707],{"className":6039,"code":11706,"language":6041,"meta":188,"style":188},"v=DMARC1; p=none; pct=100; fo=1; ri=300; rua=mailto:a@bocah.team; ruf=mailto:f@bocah.team\n",[249,11708,11709],{"__ignoreMap":188},[252,11710,11711],{"class":254,"line":255},[252,11712,11706],{},[26,11714,11715,11716,11721,11722,11724,11725,11727],{},"I'll save those email addresses for later, but now let's keep digging with some other great tool on my list, and one I often use is ",[58,11717,11720],{"href":11718,"rel":11719},"https:\u002F\u002Fspyse.com",[62],"spyse",". It's a cybersecurity search engine to find information about internet assets. When I give it the IP (",[249,11723,9745],{},") of the DigitalOcean server, it finds the ",[249,11726,11271],{}," domain. We already know that domain from our 242 domains list, but this tells me that this domain is probably one of their main ones, and it's an important one.",[26,11729,11730],{},[41,11731],{"alt":11732,"className":11733,"src":11734,"height":11735,"width":11736},"Spyse Report",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fspyse-report.webp",157,1449,[26,11738,11739,11740,11742,11743,11748,11749,11754],{},"Most of the time everyone will say that scanning is not passive, I can agree with that, but I don't really care. To scan our main target (",[249,11741,9720],{},") I'll use ",[58,11744,11747],{"href":11745,"rel":11746},"https:\u002F\u002Fwww.nmmapper.com",[62],"nmmapper",", and specifically ",[58,11750,11753],{"href":11751,"rel":11752},"https:\u002F\u002Fgithub.com\u002Frbsec\u002Fdnscan",[62],"DNSscan"," tool, it's extremely fast, and it catches so many subdomains!",[26,11756,11757,11758,11760,11761,11763,11764,126],{},"So after I ran the tool I was a little bit in shock, there were so many subdomains it hurts to scroll through them. My current initial assumption is that he must be part of an operation that's done this stuff for years. You can download the ",[249,11759,6041],{}," file with the 780 subdomains it found for ",[249,11762,9720],{}," ",[58,11765,11767],{"href":11766},"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fbocah-team-subdomains-list.txt","here",[26,11769,11770],{},"And let's not forget that this is only one domain that points to that server. We found out that there are 242 domains that point to that server, we can only imagine the subdomains that they also have (I don't intend to run them, it doesn't interest me).",[26,11772,11773,11774,11777,11778,11780,11781,11786,11787,120,11789,11792],{},"The last thing I want to check with their server (for now) is if maybe there are some query parameters their server gets and we don't know about, because we're just doing a ",[249,11775,11776],{},"GET"," request with no parameters to their domain (",[249,11779,9720],{},"). Luckily there is a tool to help us do it, ",[58,11782,11785],{"href":11783,"rel":11784},"https:\u002F\u002Fgithub.com\u002Fs0md3v\u002FArjun",[62],"Arjun"," for the rescue. Arjun sends ",[249,11788,11776],{},[249,11790,11791],{},"POST"," requests with different query parameters (from a huge list of 25,980 parameters).",[26,11794,11795,11796,1855,11798,11800],{},"Unfortunately, when I did a ",[249,11797,11776],{},[249,11799,11791],{}," requests, Arjun didn't come up with anything.",[26,11802,11803],{},[41,11804],{"alt":11805,"className":11806,"src":11807,"height":11808,"width":5737},"Arjun Search",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Farjun-search.webp",569,[458,11810,11812],{"id":11811},"_4-found-him-maybe","4. Found him! Maybe..",[26,11814,11815,11816,11818,11819,11824,11825,11827,11828,11830],{},"It's time to search a little about our new finding ",[249,11817,11271],{},". Let's go to ",[58,11820,11823],{"href":11821,"rel":11822},"http:\u002F\u002Fhunter.io",[62],"hunter.io"," and check for ",[249,11826,11271],{}," - didn't come up with anything, but we have another domain (let's not forget about it), a scan for ",[249,11829,9720],{}," comes up with an email address!",[26,11832,11833],{},[41,11834],{"alt":11835,"className":11836,"src":11837,"height":11838,"width":11839},"Hunter Result",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fhunter-result.webp",381,777,[26,11841,11842],{},"In the source website there is also a phone number (and google translate says the website is in Danish).",[26,11844,11845],{},[41,11846],{"alt":11847,"className":11848,"src":11849,"height":11850,"width":11851},"Hunter Source",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fhunter-source.webp",354,287,[26,11853,11854,11855,11858,11859,11864,11865,11870,11871,3048,11876,11880,11881,64,11884,64,11887,11890],{},"When we search Google for ",[249,11856,11857],{},"johnny_pacocha@bocah.team"," we found another site (apart from the one hunter points us to). This website is ",[58,11860,11863],{"href":11861,"rel":11862},"https:\u002F\u002Fwww.ceqoya.com",[62],"ceqoya"," which is a \"A search engine that helps NGO and ecological projects\", in this search engine there are a couple of links to Bocah Team (apparently it's a ",[58,11866,11869],{"href":11867,"rel":11868},"https:\u002F\u002Fwww.facebook.com\u002FBocah-Team-New-579200705444594\u002Fphotos",[62],"Sports Team","), but we find a new link, to ",[58,11872,11875],{"href":11873,"rel":11874},"https:\u002F\u002Fpastebin.com",[62],"pastebin",[58,11877,11878],{"href":11878,"rel":11879},"https:\u002F\u002Fpastebin.com\u002F8yDPutJQ",[62],". This contains PHP code that is basically an Apple scam page with a couple more email addresses ",[249,11882,11883],{},"empas.apel@bocah.team",[249,11885,11886],{},"apel@bocah.team",[249,11888,11889],{},"report.apel@bocah.team"," with our target domain.",[26,11892,11893,11894,11897,11898,11901,11902,11907],{},"This user has more pastes in his profile (his profile name is ",[249,11895,11896],{},"Inboxplis","), but the common thing is that they have a gmail address (",[249,11899,11900],{},"slikeye1711@gmail.com",") in the code with a link to his ",[58,11903,11906],{"href":11904,"rel":11905},"https:\u002F\u002Fwww.facebook.com\u002FSlikeye",[62],"Facebook profile page","!",[26,11909,11910],{},[41,11911],{"alt":11912,"className":11913,"src":11914,"height":11915,"width":11916},"Dian Wahyudi Facebook Profile",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fdian-wahyudi-facebook-profile.webp",1270,969,[26,11918,11919,11920,11925],{},"In his Facebook photos there is ",[58,11921,11924],{"href":11922,"rel":11923},"https:\u002F\u002Fwww.facebook.com\u002Fphoto.php?fbid=718884434960573&set=pb.100005170683150.-2207520000..&type=3",[62],"a photo"," about some Hacking \u002F Script Kiddies book in Indonesian back in April 2017. Also from his photos I can notice he has an Asus laptop with a Windows 10 on it, and that he does a lot of small money transfers.",[26,11927,11928,11929,11931,11932,11934,11935,11938],{},"When I go back to the organization I found earlier, the report from Netcraft when they registered ",[249,11930,9720],{}," in 2017, the one we found earlier (",[249,11933,11617],{},") and put the part \"Jalanin aja dulu mhanxx\" in Google Translate (on ",[951,11936,11937],{},"detect language",") it comes up as Indonesian and it means \"Just do it first mhanxx\".",[26,11940,11941,11942,11945],{},"The guy on the Facebook profile page I found is from Indonesia (he wrote it there and the language he write in his posts is Indonesian). So this is a big clue that it's probably our guy. Or is it? The big question now is: ",[871,11943,11944],{},"Is this our person?"," I think we need to find more evidence.",[26,11947,11948],{},"Quick Recap:",[866,11950,11951,11954,11960,11966],{},[154,11952,11953],{},"He is from Indonesia (posts language and from his profile) and the domain registration details have Indonesian words in it.",[154,11955,11956,11957,11959],{},"He has a hacking book and the domain registered details have the ",[249,11958,11621],{}," word in it, which is another word for hacker or script kiddie.",[154,11961,11962,11963,11965],{},"There are a lot of phishing \u002F spam domains (like ",[249,11964,11271],{},") that points to the same server and he seems like a spammer (a lot of small money transactions, and a script kiddie \u002F spammer book).",[154,11967,11968,11969,11971,11972,11974],{},"The Pastebin paste with the ",[249,11970,9720],{}," domain and ",[249,11973,11271],{}," domain in it have also his email and Facebook profile page.",[26,11976,11977],{},"It looks like our guy, but this is not enough! We need more.",[458,11979,11981],{"id":11980},"_5-take-it-up-a-notch","5. Take It Up a Notch",[26,11983,11984,11985,11987,11988,11993],{},"Let's search his gmail (",[249,11986,11900],{},") on ",[58,11989,11992],{"href":11990,"rel":11991},"https:\u002F\u002Fpipl.com",[62],"Pipl",", unfortunately we got nothing.",[242,11995,11998],{"className":3031,"code":11996,"filename":11997,"language":3034,"meta":188,"style":188},"{\n  \"@http_status_code\": 200,\n  \"@visible_sources\": 0,\n  \"@available_sources\": 0,\n  \"@persons_count\": 0,\n  \"@search_id\": \"2005021646181212464187518983203646144\",\n  \"query\": {\n    \"emails\": [\n      {\n        \"@type\": \"personal\",\n        \"address\": \"slikeye1711@gmail.com\",\n        \"address_md5\": \"5852a05e2315661b759ee006ef3bc006\"\n      }\n    ]\n  }\n}\n","pipl.json",[249,11999,12000,12004,12020,12036,12051,12066,12086,12099,12112,12116,12136,12155,12173,12177,12182,12186],{"__ignoreMap":188},[252,12001,12002],{"class":254,"line":255},[252,12003,303],{"class":262},[252,12005,12006,12008,12011,12013,12015,12018],{"class":254,"line":189},[252,12007,3056],{"class":262},[252,12009,12010],{"class":258},"@http_status_code",[252,12012,1266],{"class":262},[252,12014,312],{"class":262},[252,12016,12017],{"class":1782}," 200",[252,12019,323],{"class":262},[252,12021,12022,12024,12027,12029,12031,12034],{"class":254,"line":326},[252,12023,3056],{"class":262},[252,12025,12026],{"class":258},"@visible_sources",[252,12028,1266],{"class":262},[252,12030,312],{"class":262},[252,12032,12033],{"class":1782}," 0",[252,12035,323],{"class":262},[252,12037,12038,12040,12043,12045,12047,12049],{"class":254,"line":343},[252,12039,3056],{"class":262},[252,12041,12042],{"class":258},"@available_sources",[252,12044,1266],{"class":262},[252,12046,312],{"class":262},[252,12048,12033],{"class":1782},[252,12050,323],{"class":262},[252,12052,12053,12055,12058,12060,12062,12064],{"class":254,"line":354},[252,12054,3056],{"class":262},[252,12056,12057],{"class":258},"@persons_count",[252,12059,1266],{"class":262},[252,12061,312],{"class":262},[252,12063,12033],{"class":1782},[252,12065,323],{"class":262},[252,12067,12068,12070,12073,12075,12077,12079,12082,12084],{"class":254,"line":364},[252,12069,3056],{"class":262},[252,12071,12072],{"class":258},"@search_id",[252,12074,1266],{"class":262},[252,12076,312],{"class":262},[252,12078,1471],{"class":262},[252,12080,12081],{"class":900},"2005021646181212464187518983203646144",[252,12083,1266],{"class":262},[252,12085,323],{"class":262},[252,12087,12088,12090,12093,12095,12097],{"class":254,"line":381},[252,12089,3056],{"class":262},[252,12091,12092],{"class":258},"query",[252,12094,1266],{"class":262},[252,12096,312],{"class":262},[252,12098,351],{"class":262},[252,12100,12101,12103,12106,12108,12110],{"class":254,"line":398},[252,12102,1495],{"class":262},[252,12104,12105],{"class":896},"emails",[252,12107,1266],{"class":262},[252,12109,312],{"class":262},[252,12111,1490],{"class":262},[252,12113,12114],{"class":254,"line":415},[252,12115,7968],{"class":262},[252,12117,12118,12120,12123,12125,12127,12129,12132,12134],{"class":254,"line":421},[252,12119,7973],{"class":262},[252,12121,12122],{"class":1782},"@type",[252,12124,1266],{"class":262},[252,12126,312],{"class":262},[252,12128,1471],{"class":262},[252,12130,12131],{"class":900},"personal",[252,12133,1266],{"class":262},[252,12135,323],{"class":262},[252,12137,12138,12140,12143,12145,12147,12149,12151,12153],{"class":254,"line":427},[252,12139,7973],{"class":262},[252,12141,12142],{"class":1782},"address",[252,12144,1266],{"class":262},[252,12146,312],{"class":262},[252,12148,1471],{"class":262},[252,12150,11900],{"class":900},[252,12152,1266],{"class":262},[252,12154,323],{"class":262},[252,12156,12157,12159,12162,12164,12166,12168,12171],{"class":254,"line":662},[252,12158,7973],{"class":262},[252,12160,12161],{"class":1782},"address_md5",[252,12163,1266],{"class":262},[252,12165,312],{"class":262},[252,12167,1471],{"class":262},[252,12169,12170],{"class":900},"5852a05e2315661b759ee006ef3bc006",[252,12172,3062],{"class":262},[252,12174,12175],{"class":254,"line":668},[252,12176,8030],{"class":262},[252,12178,12179],{"class":254,"line":674},[252,12180,12181],{"class":262},"    ]\n",[252,12183,12184],{"class":254,"line":1414},[252,12185,8401],{"class":262},[252,12187,12188],{"class":254,"line":1610},[252,12189,8406],{"class":262},[26,12191,12192,12193,12198],{},"Let's check his email in ",[58,12194,12197],{"href":12195,"rel":12196},"https:\u002F\u002Fhaveibeenpwned.com\u002F",[62],"have i been pwned",", and we got a match!",[26,12200,12201],{},[41,12202],{"alt":12203,"className":12204,"src":12205,"height":12206,"width":12207},"Have I Been Pwned Search",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fhaveibeenpwned.webp",941,955,[26,12209,12210,12211,12214,12215,12217],{},"His email is on the Canva database leak, so I started to search this database leak on the internet, and it wasn't that hard to find. I found two types of files, the first is a single file that appears to be the whole info of the users, but it says ",[951,12212,12213],{},"\"cleaned\""," in the file name, and some fields are not there, for example the ",[249,12216,338],{}," is obviously a basic and it's not there.",[7498,12219,12220,12290],{},[7501,12221,12222],{},[7504,12223,12224,12227,12230,12233,12236,12239,12242,12245,12248,12251,12254,12257,12260,12263,12266,12269,12272,12275,12278,12281,12284,12287],{},[7507,12225,12226],{},"ID",[7507,12228,12229],{},"ID_HASH",[7507,12231,12232],{},"CREATE_DATE",[7507,12234,12235],{},"MAIL",[7507,12237,12238],{},"PHONE",[7507,12240,12241],{},"MAIL_STATUS",[7507,12243,12244],{},"USERNAME",[7507,12246,12247],{},"DISPLAY_NAME",[7507,12249,12250],{},"NAME1",[7507,12252,12253],{},"NAME2",[7507,12255,12256],{},"TEMPORARY",[7507,12258,12259],{},"ROLES",[7507,12261,12262],{},"DEACTIVATED",[7507,12264,12265],{},"UI_INFO",[7507,12267,12268],{},"HOMEPAGE",[7507,12270,12271],{},"CITY",[7507,12273,12274],{},"COUNTRY_CODE",[7507,12276,12277],{},"LOCALE",[7507,12279,12280],{},"PERSONAL_BRAND",[7507,12282,12283],{},"PERSONAL_BRAND_ID",[7507,12285,12286],{},"AVATAR",[7507,12288,12289],{},"HASH",[7526,12291,12292],{},[7504,12293,12294,12297,12300,12303,12308,12310,12313,12316,12319,12321,12323,12325,12327,12329,12332,12334,12336,12338,12340,12343,12346,12348],{},[7531,12295,12296],{},"48222628",[7531,12298,12299],{},"UAClrvf92U8",[7531,12301,12302],{},"2017-11-03 06:25:39",[7531,12304,12305],{},[58,12306,11900],{"href":12307},"mailto:slikeye1711@gmail.com",[7531,12309],{},[7531,12311,12312],{},"C",[7531,12314,12315],{},"slikeye1711",[7531,12317,12318],{},"Dian Wahyudi",[7531,12320],{},[7531,12322],{},[7531,12324,11631],{},[7531,12326,7003],{},[7531,12328,11631],{},[7531,12330,12331],{},"{contextTipRoyaltyFreePaymentOption:true;hasSeenPublishPaymentLicensesOnboarding:true}",[7531,12333],{},[7531,12335],{},[7531,12337],{},[7531,12339,8776],{},[7531,12341,12342],{},"48163206",[7531,12344,12345],{},"BAClrn9xkI4",[7531,12347],{},[7531,12349],{},[26,12351,12352,12353,12356],{},"The second type of files that I found is couple of small files with a list of ",[249,12354,12355],{},"emails:pass",", but unfortunately his email wasn't in any one of them.",[26,12358,12359,12360,12363,12364,12369],{},"So let's think of something different, in his profile page there was a new domain I hadn't seen before, ",[249,12361,12362],{},"slikeye.com",". I checked it with DomainTools Whois, and it was also protected. So I searched it in Google and we have a couple of hits. The first and second results are his ",[58,12365,12368],{"href":12366,"rel":12367},"https:\u002F\u002Fwww.instagram.com\u002Fdianwhyd",[62],"Instagram profile",", but it's private.",[26,12371,12372,12373,12378,12379,12382],{},"The third result is really interesting, it's from ",[58,12374,12377],{"href":12375,"rel":12376},"https:\u002F\u002Fwww.cutestat.com",[62],"cutestat website",", which is a website that provides various statistical reports like website valuation, traffic reports, social engagement, host information, domain WHOIS, etc. and it's perfect because it has a WHOIS record from ",[249,12380,12381],{},"2017-06-14T17:05:39Z"," with new data (probably before he had privacy protection on the domain).",[242,12384,12386],{"className":6039,"code":12385,"language":6041,"meta":188,"style":188},"Domain Name: SLIKEYE.COM\nRegistry Domain ID: 2132972393_DOMAIN_COM-VRSN\nRegistrar WHOIS Server: whois.resellercamp.com\nRegistrar URL: http:\u002F\u002Fresellercamp.com\u002F\nUpdated Date: 2017-06-12T12:33:20Z\nCreation Date: 2017-06-12T12:33:14Z\nRegistrar Registration Expiration Date: 2018-06-12T12:33:14Z\nRegistrar: CV. Jogjacamp\nRegistrar IANA ID: 1478\nRegistrar Abuse Contact Email: abuse@resellercamp.com\nRegistrar Abuse Contact Phone: +62.82141570000\nDomain Status: clientTransferProhibited (http:\u002F\u002Ficann.org\u002Fepp#clientTransferProhibited)\nRegistry Registrant ID:\nRegistrant Name: dian Wahyudi\nRegistrant Organization: pribadi\nRegistrant Street: Kutajaya no.15 Rt.03 Rw.01\nRegistrant City: bogor\nRegistrant State\u002FProvince: Jawa Barat\nRegistrant Postal Code: 16131\nRegistrant Country: ID\nRegistrant Phone: +62.87770307854\nRegistrant Phone Ext:\nRegistrant Fax:\nRegistrant Fax Ext:\nRegistrant Email: diansoft1711@gmail.com\nRegistry Admin ID:\nAdmin Name: dian Wahyudi\nAdmin Organization: pribadi\nAdmin Street: Kutajaya no.15 Rt.03 Rw.01\nAdmin City: bogor\nAdmin State\u002FProvince: Jawa Barat\nAdmin Postal Code: 16131\nAdmin Country: ID\nAdmin Phone: +62.87770307854\nAdmin Phone Ext:\nAdmin Fax:\nAdmin Fax Ext:\nAdmin Email: diansoft1711@gmail.com\nRegistry Tech ID:\nTech Name: dian Wahyudi\nTech Organization: pribadi\nTech Street: Kutajaya no.15 Rt.03 Rw.01\nTech City: bogor\nTech State\u002FProvince: Jawa Barat\nTech Postal Code: 16131\nTech Country: ID\nTech Phone: +62.87770307854\nTech Phone Ext:\nTech Fax:\nTech Fax Ext:\nTech Email: diansoft1711@gmail.com\nName Server: ns1.domainesia.net\nName Server: ns2.domainesia.net\nDNSSEC: Unsigned\nURL of the ICANN Whois Inaccuracy Complaint Form: https:\u002F\u002Fwww.icann.org\u002Fwicf\u002F\n",[249,12387,12388,12393,12398,12403,12408,12413,12418,12423,12428,12433,12438,12443,12448,12453,12458,12463,12468,12473,12478,12483,12488,12493,12498,12503,12508,12513,12518,12523,12528,12533,12538,12543,12548,12553,12558,12563,12568,12573,12578,12583,12588,12593,12598,12603,12608,12613,12618,12623,12628,12633,12638,12643,12648,12653,12658],{"__ignoreMap":188},[252,12389,12390],{"class":254,"line":255},[252,12391,12392],{},"Domain Name: SLIKEYE.COM\n",[252,12394,12395],{"class":254,"line":189},[252,12396,12397],{},"Registry Domain ID: 2132972393_DOMAIN_COM-VRSN\n",[252,12399,12400],{"class":254,"line":326},[252,12401,12402],{},"Registrar WHOIS Server: whois.resellercamp.com\n",[252,12404,12405],{"class":254,"line":343},[252,12406,12407],{},"Registrar URL: http:\u002F\u002Fresellercamp.com\u002F\n",[252,12409,12410],{"class":254,"line":354},[252,12411,12412],{},"Updated Date: 2017-06-12T12:33:20Z\n",[252,12414,12415],{"class":254,"line":364},[252,12416,12417],{},"Creation Date: 2017-06-12T12:33:14Z\n",[252,12419,12420],{"class":254,"line":381},[252,12421,12422],{},"Registrar Registration Expiration Date: 2018-06-12T12:33:14Z\n",[252,12424,12425],{"class":254,"line":398},[252,12426,12427],{},"Registrar: CV. Jogjacamp\n",[252,12429,12430],{"class":254,"line":415},[252,12431,12432],{},"Registrar IANA ID: 1478\n",[252,12434,12435],{"class":254,"line":421},[252,12436,12437],{},"Registrar Abuse Contact Email: abuse@resellercamp.com\n",[252,12439,12440],{"class":254,"line":427},[252,12441,12442],{},"Registrar Abuse Contact Phone: +62.82141570000\n",[252,12444,12445],{"class":254,"line":662},[252,12446,12447],{},"Domain Status: clientTransferProhibited (http:\u002F\u002Ficann.org\u002Fepp#clientTransferProhibited)\n",[252,12449,12450],{"class":254,"line":668},[252,12451,12452],{},"Registry Registrant ID:\n",[252,12454,12455],{"class":254,"line":674},[252,12456,12457],{},"Registrant Name: dian Wahyudi\n",[252,12459,12460],{"class":254,"line":1414},[252,12461,12462],{},"Registrant Organization: pribadi\n",[252,12464,12465],{"class":254,"line":1610},[252,12466,12467],{},"Registrant Street: Kutajaya no.15 Rt.03 Rw.01\n",[252,12469,12470],{"class":254,"line":1618},[252,12471,12472],{},"Registrant City: bogor\n",[252,12474,12475],{"class":254,"line":1628},[252,12476,12477],{},"Registrant State\u002FProvince: Jawa Barat\n",[252,12479,12480],{"class":254,"line":1645},[252,12481,12482],{},"Registrant Postal Code: 16131\n",[252,12484,12485],{"class":254,"line":1655},[252,12486,12487],{},"Registrant Country: ID\n",[252,12489,12490],{"class":254,"line":1660},[252,12491,12492],{},"Registrant Phone: +62.87770307854\n",[252,12494,12495],{"class":254,"line":2363},[252,12496,12497],{},"Registrant Phone Ext:\n",[252,12499,12500],{"class":254,"line":2392},[252,12501,12502],{},"Registrant Fax:\n",[252,12504,12505],{"class":254,"line":2407},[252,12506,12507],{},"Registrant Fax Ext:\n",[252,12509,12510],{"class":254,"line":2421},[252,12511,12512],{},"Registrant Email: diansoft1711@gmail.com\n",[252,12514,12515],{"class":254,"line":2436},[252,12516,12517],{},"Registry Admin ID:\n",[252,12519,12520],{"class":254,"line":2441},[252,12521,12522],{},"Admin Name: dian Wahyudi\n",[252,12524,12525],{"class":254,"line":2446},[252,12526,12527],{},"Admin Organization: pribadi\n",[252,12529,12530],{"class":254,"line":2472},[252,12531,12532],{},"Admin Street: Kutajaya no.15 Rt.03 Rw.01\n",[252,12534,12535],{"class":254,"line":2486},[252,12536,12537],{},"Admin City: bogor\n",[252,12539,12540],{"class":254,"line":2501},[252,12541,12542],{},"Admin State\u002FProvince: Jawa Barat\n",[252,12544,12545],{"class":254,"line":3002},[252,12546,12547],{},"Admin Postal Code: 16131\n",[252,12549,12550],{"class":254,"line":3007},[252,12551,12552],{},"Admin Country: ID\n",[252,12554,12555],{"class":254,"line":3012},[252,12556,12557],{},"Admin Phone: +62.87770307854\n",[252,12559,12560],{"class":254,"line":3805},[252,12561,12562],{},"Admin Phone Ext:\n",[252,12564,12565],{"class":254,"line":3810},[252,12566,12567],{},"Admin Fax:\n",[252,12569,12570],{"class":254,"line":3822},[252,12571,12572],{},"Admin Fax Ext:\n",[252,12574,12575],{"class":254,"line":3832},[252,12576,12577],{},"Admin Email: diansoft1711@gmail.com\n",[252,12579,12580],{"class":254,"line":3840},[252,12581,12582],{},"Registry Tech ID:\n",[252,12584,12585],{"class":254,"line":3851},[252,12586,12587],{},"Tech Name: dian Wahyudi\n",[252,12589,12590],{"class":254,"line":3858},[252,12591,12592],{},"Tech Organization: pribadi\n",[252,12594,12595],{"class":254,"line":3874},[252,12596,12597],{},"Tech Street: Kutajaya no.15 Rt.03 Rw.01\n",[252,12599,12600],{"class":254,"line":3884},[252,12601,12602],{},"Tech City: bogor\n",[252,12604,12605],{"class":254,"line":3889},[252,12606,12607],{},"Tech State\u002FProvince: Jawa Barat\n",[252,12609,12610],{"class":254,"line":3897},[252,12611,12612],{},"Tech Postal Code: 16131\n",[252,12614,12615],{"class":254,"line":3907},[252,12616,12617],{},"Tech Country: ID\n",[252,12619,12620],{"class":254,"line":3916},[252,12621,12622],{},"Tech Phone: +62.87770307854\n",[252,12624,12625],{"class":254,"line":3924},[252,12626,12627],{},"Tech Phone Ext:\n",[252,12629,12630],{"class":254,"line":3935},[252,12631,12632],{},"Tech Fax:\n",[252,12634,12635],{"class":254,"line":3946},[252,12636,12637],{},"Tech Fax Ext:\n",[252,12639,12640],{"class":254,"line":3953},[252,12641,12642],{},"Tech Email: diansoft1711@gmail.com\n",[252,12644,12645],{"class":254,"line":3964},[252,12646,12647],{},"Name Server: ns1.domainesia.net\n",[252,12649,12650],{"class":254,"line":3969},[252,12651,12652],{},"Name Server: ns2.domainesia.net\n",[252,12654,12655],{"class":254,"line":3980},[252,12656,12657],{},"DNSSEC: Unsigned\n",[252,12659,12660],{"class":254,"line":3989},[252,12661,12662],{},"URL of the ICANN Whois Inaccuracy Complaint Form: https:\u002F\u002Fwww.icann.org\u002Fwicf\u002F\n",[26,12664,12665,12666,12669,12670,12673,12674,12677,12678,12681,12682,983],{},"We can see words in Indonesian, his name (Dian Wahyudi), and his phone number that also in his Facebook profile, so the domain was his in this date. We can see a physical address (street: ",[249,12667,12668],{},"Kutajaya no.15 Rt.03 Rw.01",", city: ",[249,12671,12672],{},"bogor",", state: ",[249,12675,12676],{},"Jawa Barat",", zip: ",[249,12679,12680],{},"16131","), and a new email (",[249,12683,12684],{},"diansoft1711@gmail.com",[26,12686,12687,12688,12690,12691,12696,12697,12700,12701,12704,12705,126],{},"Before we continue digging with our new findings, let's keep looking in our Google search for ",[249,12689,12362],{},". There was nothing more in there except for the fourth result, it was his ",[58,12692,12695],{"href":12693,"rel":12694},"https:\u002F\u002Ftwitter.com\u002Fdianwhyd",[62],"Twitter profile",". It doesn't look like much, he uploads more photos and talks more in his Facebook profile (he also abandoned Twitter in 2017). But a quick scroll through his tweets with pictures shows a tweet from 2014 with a Facebook login that says \"fb maintenance\", and the start of his email is the ",[249,12698,12699],{},"input"," field. It says ",[249,12702,12703],{},"slikeye"," so it's probably ",[249,12706,11900],{},[26,12708,12709,12710,12713,12714,12716,12717,12719],{},"It's time to do a ",[951,12711,12712],{},"\"Forget Password\""," on Facebook with his email. It got us his last 2 digits in his phone number, and they're the same as the phone number in his Facebook profile and his 2017 WHOIS record for ",[249,12715,12362],{},", so we know it's probably his real and private phone number (also, a ",[951,12718,12712],{}," in Twitter, for the same email, gets us the same 2 last digits).",[26,12721,12722],{},[41,12723],{"alt":12724,"className":12725,"src":12726},"Facebook Forget Password",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Ffacebook-forget-password.webp",[26,12728,12729,12730,12732],{},"Unfortunately we didn't get new phone numbers or email addresses, but ",[951,12731,12712],{}," is a good method and a lot of times reveals different phone numbers and email addresses that are the private ones of the target.",[458,12734,12736],{"id":12735},"_6-pivoting","6. Pivoting",[26,12738,12739,12740,12743,12744,126],{},"The new email address and phone number don't get us any result on Pipl, that's a bummer. Pipl is consistently failing us in this recon investigation. On the other hand a TrueCaller search for the phone number confirms the new email address we already found in the 2017 WHOIS record, and tells us his ",[249,12741,12742],{},"carrier"," and that he is verified by ",[58,12745,12748],{"href":12746,"rel":12747},"https:\u002F\u002Fwww.truecaller.com",[62],"TrueCaller",[242,12750,12753],{"className":3031,"code":12751,"filename":12752,"language":3034,"meta":188,"style":188},"{\n  \"data\": [\n    {\n      \"id\": \"lB3vOHRxq8Ek9wc30h2uIA==\",\n      \"name\": \"Dian Wahyudi\",\n      \"firstName\": \"Dian\",\n      \"lastName\": \"Wahyudi\",\n      \"gender\": \"UNKNOWN\",\n      \"score\": 0.9,\n      \"access\": \"PUBLIC\",\n      \"enhanced\": true,\n      \"phones\": [\n        {\n          \"e164Format\": \"+6287770307854\",\n          \"numberType\": \"MOBILE\",\n          \"nationalFormat\": \"0877-7030-7854\",\n          \"dialingCode\": 62,\n          \"countryCode\": \"ID\",\n          \"carrier\": \"XL Axiata\",\n          \"type\": \"openPhone\"\n        }\n      ],\n      \"addresses\": [\n        {\n          \"address\": \"ID\",\n          \"countryCode\": \"ID\",\n          \"timeZone\": \"+07:00\",\n          \"type\": \"address\"\n        }\n      ],\n      \"internetAddresses\": [\n        {\n          \"id\": \"diansoft1711@gmail.com\",\n          \"service\": \"email\",\n          \"caption\": \"Dian Wahyudi\",\n          \"type\": \"internetAddress\"\n        }\n      ],\n      \"badges\": [\"verified\", \"user\"],\n      \"tags\": [],\n      \"profileEditHistory\": {},\n      \"spamInfo\": {},\n      \"cacheTtl\": 1296000,\n      \"sources\": []\n    }\n  ],\n  \"provider\": \"ss-nu\",\n  \"stats\": {\n    \"sourceStats\": []\n  }\n}\n","truecaller.json",[249,12754,12755,12759,12772,12776,12795,12813,12832,12851,12871,12887,12907,12920,12933,12938,12959,12979,12999,13015,13034,13053,13070,13075,13080,13093,13097,13115,13133,13153,13169,13173,13177,13190,13194,13212,13231,13250,13267,13271,13275,13306,13320,13333,13346,13362,13376,13381,13386,13406,13419,13432,13436],{"__ignoreMap":188},[252,12756,12757],{"class":254,"line":255},[252,12758,303],{"class":262},[252,12760,12761,12763,12766,12768,12770],{"class":254,"line":189},[252,12762,3056],{"class":262},[252,12764,12765],{"class":258},"data",[252,12767,1266],{"class":262},[252,12769,312],{"class":262},[252,12771,1490],{"class":262},[252,12773,12774],{"class":254,"line":326},[252,12775,1530],{"class":262},[252,12777,12778,12780,12782,12784,12786,12788,12791,12793],{"class":254,"line":343},[252,12779,2959],{"class":262},[252,12781,8152],{"class":896},[252,12783,1266],{"class":262},[252,12785,312],{"class":262},[252,12787,1471],{"class":262},[252,12789,12790],{"class":900},"lB3vOHRxq8Ek9wc30h2uIA==",[252,12792,1266],{"class":262},[252,12794,323],{"class":262},[252,12796,12797,12799,12801,12803,12805,12807,12809,12811],{"class":254,"line":354},[252,12798,2959],{"class":262},[252,12800,3536],{"class":896},[252,12802,1266],{"class":262},[252,12804,312],{"class":262},[252,12806,1471],{"class":262},[252,12808,12318],{"class":900},[252,12810,1266],{"class":262},[252,12812,323],{"class":262},[252,12814,12815,12817,12819,12821,12823,12825,12828,12830],{"class":254,"line":364},[252,12816,2959],{"class":262},[252,12818,376],{"class":896},[252,12820,1266],{"class":262},[252,12822,312],{"class":262},[252,12824,1471],{"class":262},[252,12826,12827],{"class":900},"Dian",[252,12829,1266],{"class":262},[252,12831,323],{"class":262},[252,12833,12834,12836,12838,12840,12842,12844,12847,12849],{"class":254,"line":381},[252,12835,2959],{"class":262},[252,12837,393],{"class":896},[252,12839,1266],{"class":262},[252,12841,312],{"class":262},[252,12843,1471],{"class":262},[252,12845,12846],{"class":900},"Wahyudi",[252,12848,1266],{"class":262},[252,12850,323],{"class":262},[252,12852,12853,12855,12858,12860,12862,12864,12867,12869],{"class":254,"line":398},[252,12854,2959],{"class":262},[252,12856,12857],{"class":896},"gender",[252,12859,1266],{"class":262},[252,12861,312],{"class":262},[252,12863,1471],{"class":262},[252,12865,12866],{"class":900},"UNKNOWN",[252,12868,1266],{"class":262},[252,12870,323],{"class":262},[252,12872,12873,12875,12878,12880,12882,12885],{"class":254,"line":415},[252,12874,2959],{"class":262},[252,12876,12877],{"class":896},"score",[252,12879,1266],{"class":262},[252,12881,312],{"class":262},[252,12883,12884],{"class":1782}," 0.9",[252,12886,323],{"class":262},[252,12888,12889,12891,12894,12896,12898,12900,12903,12905],{"class":254,"line":421},[252,12890,2959],{"class":262},[252,12892,12893],{"class":896},"access",[252,12895,1266],{"class":262},[252,12897,312],{"class":262},[252,12899,1471],{"class":262},[252,12901,12902],{"class":900},"PUBLIC",[252,12904,1266],{"class":262},[252,12906,323],{"class":262},[252,12908,12909,12911,12914,12916,12918],{"class":254,"line":427},[252,12910,2959],{"class":262},[252,12912,12913],{"class":896},"enhanced",[252,12915,1266],{"class":262},[252,12917,312],{"class":262},[252,12919,3385],{"class":262},[252,12921,12922,12924,12927,12929,12931],{"class":254,"line":662},[252,12923,2959],{"class":262},[252,12925,12926],{"class":896},"phones",[252,12928,1266],{"class":262},[252,12930,312],{"class":262},[252,12932,1490],{"class":262},[252,12934,12935],{"class":254,"line":668},[252,12936,12937],{"class":262},"        {\n",[252,12939,12940,12943,12946,12948,12950,12952,12955,12957],{"class":254,"line":674},[252,12941,12942],{"class":262},"          \"",[252,12944,12945],{"class":1782},"e164Format",[252,12947,1266],{"class":262},[252,12949,312],{"class":262},[252,12951,1471],{"class":262},[252,12953,12954],{"class":900},"+6287770307854",[252,12956,1266],{"class":262},[252,12958,323],{"class":262},[252,12960,12961,12963,12966,12968,12970,12972,12975,12977],{"class":254,"line":1414},[252,12962,12942],{"class":262},[252,12964,12965],{"class":1782},"numberType",[252,12967,1266],{"class":262},[252,12969,312],{"class":262},[252,12971,1471],{"class":262},[252,12973,12974],{"class":900},"MOBILE",[252,12976,1266],{"class":262},[252,12978,323],{"class":262},[252,12980,12981,12983,12986,12988,12990,12992,12995,12997],{"class":254,"line":1610},[252,12982,12942],{"class":262},[252,12984,12985],{"class":1782},"nationalFormat",[252,12987,1266],{"class":262},[252,12989,312],{"class":262},[252,12991,1471],{"class":262},[252,12993,12994],{"class":900},"0877-7030-7854",[252,12996,1266],{"class":262},[252,12998,323],{"class":262},[252,13000,13001,13003,13006,13008,13010,13013],{"class":254,"line":1618},[252,13002,12942],{"class":262},[252,13004,13005],{"class":1782},"dialingCode",[252,13007,1266],{"class":262},[252,13009,312],{"class":262},[252,13011,13012],{"class":1782}," 62",[252,13014,323],{"class":262},[252,13016,13017,13019,13022,13024,13026,13028,13030,13032],{"class":254,"line":1628},[252,13018,12942],{"class":262},[252,13020,13021],{"class":1782},"countryCode",[252,13023,1266],{"class":262},[252,13025,312],{"class":262},[252,13027,1471],{"class":262},[252,13029,12226],{"class":900},[252,13031,1266],{"class":262},[252,13033,323],{"class":262},[252,13035,13036,13038,13040,13042,13044,13046,13049,13051],{"class":254,"line":1645},[252,13037,12942],{"class":262},[252,13039,12742],{"class":1782},[252,13041,1266],{"class":262},[252,13043,312],{"class":262},[252,13045,1471],{"class":262},[252,13047,13048],{"class":900},"XL Axiata",[252,13050,1266],{"class":262},[252,13052,323],{"class":262},[252,13054,13055,13057,13059,13061,13063,13065,13068],{"class":254,"line":1655},[252,13056,12942],{"class":262},[252,13058,1231],{"class":1782},[252,13060,1266],{"class":262},[252,13062,312],{"class":262},[252,13064,1471],{"class":262},[252,13066,13067],{"class":900},"openPhone",[252,13069,3062],{"class":262},[252,13071,13072],{"class":254,"line":1660},[252,13073,13074],{"class":262},"        }\n",[252,13076,13077],{"class":254,"line":2363},[252,13078,13079],{"class":262},"      ],\n",[252,13081,13082,13084,13087,13089,13091],{"class":254,"line":2392},[252,13083,2959],{"class":262},[252,13085,13086],{"class":896},"addresses",[252,13088,1266],{"class":262},[252,13090,312],{"class":262},[252,13092,1490],{"class":262},[252,13094,13095],{"class":254,"line":2407},[252,13096,12937],{"class":262},[252,13098,13099,13101,13103,13105,13107,13109,13111,13113],{"class":254,"line":2421},[252,13100,12942],{"class":262},[252,13102,12142],{"class":1782},[252,13104,1266],{"class":262},[252,13106,312],{"class":262},[252,13108,1471],{"class":262},[252,13110,12226],{"class":900},[252,13112,1266],{"class":262},[252,13114,323],{"class":262},[252,13116,13117,13119,13121,13123,13125,13127,13129,13131],{"class":254,"line":2436},[252,13118,12942],{"class":262},[252,13120,13021],{"class":1782},[252,13122,1266],{"class":262},[252,13124,312],{"class":262},[252,13126,1471],{"class":262},[252,13128,12226],{"class":900},[252,13130,1266],{"class":262},[252,13132,323],{"class":262},[252,13134,13135,13137,13140,13142,13144,13146,13149,13151],{"class":254,"line":2441},[252,13136,12942],{"class":262},[252,13138,13139],{"class":1782},"timeZone",[252,13141,1266],{"class":262},[252,13143,312],{"class":262},[252,13145,1471],{"class":262},[252,13147,13148],{"class":900},"+07:00",[252,13150,1266],{"class":262},[252,13152,323],{"class":262},[252,13154,13155,13157,13159,13161,13163,13165,13167],{"class":254,"line":2446},[252,13156,12942],{"class":262},[252,13158,1231],{"class":1782},[252,13160,1266],{"class":262},[252,13162,312],{"class":262},[252,13164,1471],{"class":262},[252,13166,12142],{"class":900},[252,13168,3062],{"class":262},[252,13170,13171],{"class":254,"line":2472},[252,13172,13074],{"class":262},[252,13174,13175],{"class":254,"line":2486},[252,13176,13079],{"class":262},[252,13178,13179,13181,13184,13186,13188],{"class":254,"line":2501},[252,13180,2959],{"class":262},[252,13182,13183],{"class":896},"internetAddresses",[252,13185,1266],{"class":262},[252,13187,312],{"class":262},[252,13189,1490],{"class":262},[252,13191,13192],{"class":254,"line":3002},[252,13193,12937],{"class":262},[252,13195,13196,13198,13200,13202,13204,13206,13208,13210],{"class":254,"line":3007},[252,13197,12942],{"class":262},[252,13199,8152],{"class":1782},[252,13201,1266],{"class":262},[252,13203,312],{"class":262},[252,13205,1471],{"class":262},[252,13207,12684],{"class":900},[252,13209,1266],{"class":262},[252,13211,323],{"class":262},[252,13213,13214,13216,13219,13221,13223,13225,13227,13229],{"class":254,"line":3012},[252,13215,12942],{"class":262},[252,13217,13218],{"class":1782},"service",[252,13220,1266],{"class":262},[252,13222,312],{"class":262},[252,13224,1471],{"class":262},[252,13226,320],{"class":900},[252,13228,1266],{"class":262},[252,13230,323],{"class":262},[252,13232,13233,13235,13238,13240,13242,13244,13246,13248],{"class":254,"line":3805},[252,13234,12942],{"class":262},[252,13236,13237],{"class":1782},"caption",[252,13239,1266],{"class":262},[252,13241,312],{"class":262},[252,13243,1471],{"class":262},[252,13245,12318],{"class":900},[252,13247,1266],{"class":262},[252,13249,323],{"class":262},[252,13251,13252,13254,13256,13258,13260,13262,13265],{"class":254,"line":3810},[252,13253,12942],{"class":262},[252,13255,1231],{"class":1782},[252,13257,1266],{"class":262},[252,13259,312],{"class":262},[252,13261,1471],{"class":262},[252,13263,13264],{"class":900},"internetAddress",[252,13266,3062],{"class":262},[252,13268,13269],{"class":254,"line":3822},[252,13270,13074],{"class":262},[252,13272,13273],{"class":254,"line":3832},[252,13274,13079],{"class":262},[252,13276,13277,13279,13282,13284,13286,13288,13290,13293,13295,13297,13299,13302,13304],{"class":254,"line":3840},[252,13278,2959],{"class":262},[252,13280,13281],{"class":896},"badges",[252,13283,1266],{"class":262},[252,13285,312],{"class":262},[252,13287,1263],{"class":262},[252,13289,1266],{"class":262},[252,13291,13292],{"class":900},"verified",[252,13294,1266],{"class":262},[252,13296,270],{"class":262},[252,13298,1471],{"class":262},[252,13300,13301],{"class":900},"user",[252,13303,1266],{"class":262},[252,13305,3371],{"class":262},[252,13307,13308,13310,13313,13315,13317],{"class":254,"line":3851},[252,13309,2959],{"class":262},[252,13311,13312],{"class":896},"tags",[252,13314,1266],{"class":262},[252,13316,312],{"class":262},[252,13318,13319],{"class":262}," [],\n",[252,13321,13322,13324,13327,13329,13331],{"class":254,"line":3858},[252,13323,2959],{"class":262},[252,13325,13326],{"class":896},"profileEditHistory",[252,13328,1266],{"class":262},[252,13330,312],{"class":262},[252,13332,1295],{"class":262},[252,13334,13335,13337,13340,13342,13344],{"class":254,"line":3874},[252,13336,2959],{"class":262},[252,13338,13339],{"class":896},"spamInfo",[252,13341,1266],{"class":262},[252,13343,312],{"class":262},[252,13345,1295],{"class":262},[252,13347,13348,13350,13353,13355,13357,13360],{"class":254,"line":3884},[252,13349,2959],{"class":262},[252,13351,13352],{"class":896},"cacheTtl",[252,13354,1266],{"class":262},[252,13356,312],{"class":262},[252,13358,13359],{"class":1782}," 1296000",[252,13361,323],{"class":262},[252,13363,13364,13366,13369,13371,13373],{"class":254,"line":3889},[252,13365,2959],{"class":262},[252,13367,13368],{"class":896},"sources",[252,13370,1266],{"class":262},[252,13372,312],{"class":262},[252,13374,13375],{"class":262}," []\n",[252,13377,13378],{"class":254,"line":3897},[252,13379,13380],{"class":262},"    }\n",[252,13382,13383],{"class":254,"line":3907},[252,13384,13385],{"class":262},"  ],\n",[252,13387,13388,13390,13393,13395,13397,13399,13402,13404],{"class":254,"line":3916},[252,13389,3056],{"class":262},[252,13391,13392],{"class":258},"provider",[252,13394,1266],{"class":262},[252,13396,312],{"class":262},[252,13398,1471],{"class":262},[252,13400,13401],{"class":900},"ss-nu",[252,13403,1266],{"class":262},[252,13405,323],{"class":262},[252,13407,13408,13410,13413,13415,13417],{"class":254,"line":3924},[252,13409,3056],{"class":262},[252,13411,13412],{"class":258},"stats",[252,13414,1266],{"class":262},[252,13416,312],{"class":262},[252,13418,351],{"class":262},[252,13420,13421,13423,13426,13428,13430],{"class":254,"line":3935},[252,13422,1495],{"class":262},[252,13424,13425],{"class":896},"sourceStats",[252,13427,1266],{"class":262},[252,13429,312],{"class":262},[252,13431,13375],{"class":262},[252,13433,13434],{"class":254,"line":3946},[252,13435,8401],{"class":262},[252,13437,13438],{"class":254,"line":3953},[252,13439,8406],{"class":262},[26,13441,13442,13443,13448,13449,13451,13452,13457,13458,13461,13462,13465,13466,126],{},"A ",[58,13444,13447],{"href":13445,"rel":13446},"https:\u002F\u002Fwww.spokeo.com",[62],"Spokeo"," search for his email (",[249,13450,12684],{},") provides us his ",[58,13453,13456],{"href":13454,"rel":13455},"https:\u002F\u002Fwww.pinterest.com\u002Fdiansoft1711",[62],"Pinterest profile",", but that is not much because his profile is empty. We can notice another pattern here, his profile name on Pinterest is ",[249,13459,13460],{},"diansoft1711",", on Facebook it's ",[249,13463,13464],{},"Slikeye",", and on Twitter and Instagram is ",[249,13467,13468],{},"dianwhyd",[26,13470,13471,13472,126],{},"I think we need to search for more Social Networks profiles for his usernames. There're tools to help us do it, like ",[58,13473,13476],{"href":13474,"rel":13475},"https:\u002F\u002Fgithub.com\u002Fthelinuxchoice\u002Fuserrecon",[62],"userrecon",[151,13478,13479,13484,13495],{},[154,13480,13481,13483],{},[249,13482,13460],{}," username didn't find any new profiles (it found only Pinterest).",[154,13485,13486,13488,13489,13494],{},[249,13487,13464],{}," username got us a ",[58,13490,13493],{"href":13491,"rel":13492},"https:\u002F\u002Fbitbucket.org\u002FSlikeye",[62],"BitBucket profile"," (without any repos, or all of them are private).",[154,13496,13497,13499,13500,13504,13505,13510,13511,126],{},[249,13498,13468],{}," username provides us with a different ",[58,13501,13456],{"href":13502,"rel":13503},"https:\u002F\u002Fwww.pinterest.com\u002Fdianwhyd",[62],", a ",[58,13506,13509],{"href":13507,"rel":13508},"https:\u002F\u002Fflipboard.com\u002F@DianWhy",[62],"Flipboard profile",", and a ",[58,13512,13515],{"href":13513,"rel":13514},"https:\u002F\u002Fwww.tripadvisor.com\u002FProfile\u002FDianwhyd",[62],"Tripadvisor profile",[26,13517,13518,13519,13521,13522,13527,13528,13531],{},"If we're already here, let's search for his username in the Canva database leak (",[249,13520,12315],{},") with our new tool. This search gives us interesting results - a different ",[58,13523,13526],{"href":13524,"rel":13525},"https:\u002F\u002Fwww.facebook.com\u002Fslikeye1711",[62],"Facebook profile",", it looks like someone else, but he's also from ",[951,13529,13530],{},"\"Bogor, Indonesia\"",". He says on his profile he works at Apple (and it takes me straight into the Pastebin account with the Apple scam page), but he has an Apple advertisement photo on the cover page. The thing is, when I open it up to see it in full size you can actually see it's a screenshot of apple website!",[26,13533,13534],{},[41,13535],{"alt":13536,"className":13537,"src":13538},"Agustinus Rivaldo Facebook Cover Image",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fagustinus-rivaldo-facebook-cover.webp",[26,13540,13541,13542,13545,13546,13551],{},"Or is it? When you look closer on the URL of the page it's ",[249,13543,13544],{},"http:\u002F\u002Fapps-icloudapps.serveirc.com"," which is not Apples one. This is a scam \u002F phishing page to login with your apple credentials. Other stuff we see on the screenshot is that the web browser is Chrome, it's a Windows 10 OS on a laptop, and he has a bunch of other tabs open (Facebook, a cPanels, and some other stuff I don't recognize), and he has a ",[58,13547,13550],{"href":13548,"rel":13549},"https:\u002F\u002Fwww.sublimetext.com",[62],"sublime text editor"," window minimized.",[26,13553,13554,13555,13557],{},"This doesn't look good for our guy, this is probably a fake account for an apple scam \u002F phishing. When I search for ",[249,13556,13544],{}," in Google all the results are about scam stuff (news articles, forums, etc). This is probably our guy or some other man in the phishing operation he belongs to.",[26,13559,13560,13561,13564,13565,13568],{},"The next step is to search for more information about those phishing sites. The search for ",[249,13562,13563],{},"*.bocah.team"," on Google results in couple of new emails and websites, but we have enough of that, let's search specific for Pastebin, because that's where we find the apple scam page with his email and Facebook profile URL. So when I searched ",[249,13566,13567],{},"*.bocah.team site:pastebin.com"," I found 4 different pastes:",[151,13570,13571,13585,13602,13610],{},[154,13572,7635,13573,13578,13579,13584],{},[58,13574,13577],{"href":13575,"rel":13576},"https:\u002F\u002Fpastebin.com\u002FT6bnuqze",[62],"first"," from profile named ",[58,13580,13583],{"href":13581,"rel":13582},"https:\u002F\u002Fpastebin.com\u002Fu\u002Faridho",[62],"ARIDHO",", that have a lot of phishing pastes in his profile.",[154,13586,7635,13587,13592,13593,13598,13599,126],{},[58,13588,13591],{"href":13589,"rel":13590},"https:\u002F\u002Fpastebin.com\u002FXmEBg6vF",[62],"second"," from a profile named ",[58,13594,13597],{"href":13595,"rel":13596},"https:\u002F\u002Fpastebin.com\u002Fu\u002Frizkyibeng",[62],"RIZKYIBENG"," and all of his pastes are Apple scam page, except for one, where he has a gmail address ",[249,13600,13601],{},"ibengrizky01@gmail.com",[154,13603,7635,13604,13609],{},[58,13605,13608],{"href":13606,"rel":13607},"https:\u002F\u002Fpastebin.com\u002F3MZySMy1",[62],"third"," is a guest, and there is nothing interesting in the paste.",[154,13611,7635,13612,13617],{},[58,13613,13616],{"href":13614,"rel":13615},"https:\u002F\u002Fpastebin.com\u002FC11evf9P",[62],"fourth"," is also a guest, and there is also nothing interesting in his paste.",[26,13619,13620,13621,13623,13624,13626],{},"Now, we can go search about ",[249,13622,13597],{}," with his email ",[249,13625,13601],{},", but it is out of scope right now. He can be a member of this phishing operation and maybe he isn't, and just doing those stuff alone, either way it's not an interest for me right now.",[26,13628,13629,13630,13632,13633,983],{},"But the stuff I started to notice is that all of those scam \u002F phishing pages look the same. For example here is the slikeye panel of our target, and ",[249,13631,13583],{}," Apple scam panel (rendered with ",[58,13634,13637],{"href":13635,"rel":13636},"https:\u002F\u002Fjsfiddle.net",[62],"jsfiddle",[26,13639,13640],{},[41,13641],{"alt":13642,"className":13643,"src":13644},"Silkeye Panel",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fsilkeye-panel.webp",[26,13646,13647],{},[41,13648],{"alt":13649,"className":13650,"src":13651},"Aridho Panel",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Faridho-panel.webp",[26,13653,13654,13655,13657],{},"I think we got enough of this, this is for sure a scam \u002F phishing operation and our target is one of this group. So, the last thing I want to do is to search for the new email we found (",[249,13656,12684],{},") let's see if this email has been in a leak.",[26,13659,13660],{},[41,13661],{"alt":13662,"className":13663,"src":13664},"Have I Been Pwned Report",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fhaveibeenpwned-second-report.webp",[26,13666,13667,13668,13671],{},"It has, so it's time to search for the Bukalapak leaked database. This one wasn't as easy as the ",[249,13669,13670],{},"Canva"," one. But I managed to find it. And our new email target is in there.",[7498,13673,13674,13697],{},[7501,13675,13676],{},[7504,13677,13678,13681,13683,13686,13688,13691,13694],{},[7507,13679,13680],{},"Uid",[7507,13682,7509],{},[7507,13684,13685],{},"Full Name",[7507,13687,7512],{},[7507,13689,13690],{},"Salt",[7507,13692,13693],{},"Username",[7507,13695,13696],{},"Birthday",[7526,13698,13699],{},[7504,13700,13701,13704,13709,13712,13715,13718,13720],{},[7531,13702,13703],{},"47241282",[7531,13705,13706],{},[58,13707,12684],{"href":13708},"mailto:diansoft1711@gmail.com",[7531,13710,13711],{},"Andi",[7531,13713,13714],{},"$2a$10$T12OFkTmziUovvADLb7koeAd4VODsOkjEECOftIb5fy4UIaE3f7C2",[7531,13716,13717],{},"C1LyhB6B52CIjwEv3AMR",[7531,13719,13460],{},[7531,13721],{},[26,13723,13724,13725,13728,13729,13731,13732,120,13734,13737,13738,13741,13742,13744,13745,13748],{},"We can see he have a different ",[249,13726,13727],{},"fullname"," here (",[249,13730,13711],{},"). And we manage to get his ",[249,13733,338],{},[249,13735,13736],{},"salt",". We don't even need to use tools to identify the hash type, from the news out there we know it's a ",[249,13739,13740],{},"bcrypt",", and we see in the database we have a ",[249,13743,13736],{},". We can use ",[58,13746,5595],{"href":5760,"rel":13747},[62]," and try to crack it, but this is my red line, I don't think the guy tried to hack my service.",[458,13750,13752],{"id":13751},"_7-quick-mentions","7. Quick Mentions",[26,13754,13755],{},"Before I wrap this up, I just want to quickly mention a couple of tools I use all the time, but in this case wasn't any help (or other tools get more data), and I just didn't mention them in the write up.",[26,13757,13758,13759,13764],{},"So the first one is ",[58,13760,13763],{"href":13761,"rel":13762},"https:\u002F\u002Fgithub.com\u002Faboul3la\u002FSublist3r",[62],"sublist3r",", it gave me some subdomains (but DNSscan found more, much more! subdomains (and much faster)).",[26,13766,13767],{},[41,13768],{"alt":13769,"className":13770,"src":13771},"sublist3r Results",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fsublist3r-results.webp",[26,13773,13774,13775,13780,13781,13784,13785,13789],{},"Another tool I always use is ",[58,13776,13779],{"href":13777,"rel":13778},"https:\u002F\u002Fgithub.com\u002Flaramies\u002FtheHarvester",[62],"theHarvester",". The Harvester found the ",[249,13782,13783],{},"johnny_pacocha"," email which led us to his Pastebin and then his Facebook profile page. But to be truthful this is not the first way I found this email, so I include the original way (with ",[58,13786,11823],{"href":13787,"rel":13788},"https:\u002F\u002Fhunter.io",[62],"). But, if I didn't have an account there, or hunter wasn't able to find it, I probably would've been able to find it with theHarvester.",[26,13791,13792],{},[41,13793],{"alt":13794,"className":13795,"src":13796},"The Harvester Report",[45,46],"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002FtheHarvester-yahoo.webp",[458,13798,13800],{"id":13799},"_8-summary","8. Summary",[26,13802,13803],{},"So, to recap our trip, we started with just 2 email addresses from the same domain and 2 IPs. We managed to probably find our guy and determine he is a member in a scam \u002F phishing operation. We found more people from this operation but didn't continue to investigate them. But the most important thing, the final verdict is that no one was trying to hack my service at work. He probably did some scanning and encountered my client app, he tried to enter but quickly noticed the security protections or just simply gave up because he didn't think there is anything important there and it's not worth the trouble and time (he also stopped trying after the 4 attempts).",[26,13805,13806],{},"The other thing we gain in this reconnaissance (apart from the final verdict), is a lot of fun time and some brushing off of old rust. I hope you learned something new or that I just piqued your interest. This world is really big, there are a lot of tools that I didn't use here and there are a lot of cool people that do cool stuff and find really inventive ways to investigate.",[772,13808,13809],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}",{"title":188,"searchDepth":189,"depth":189,"links":13811},[13812,13813,13814,13815,13816,13817,13818,13819],{"id":7486,"depth":189,"text":7487},{"id":7642,"depth":189,"text":7643},{"id":11581,"depth":189,"text":11582},{"id":11811,"depth":189,"text":11812},{"id":11980,"depth":189,"text":11981},{"id":12735,"depth":189,"text":12736},{"id":13751,"depth":189,"text":13752},{"id":13799,"depth":189,"text":13800},"2020-06-01","A hands-on walkthrough of passive reconnaissance and OSINT techniques.",{"src":13823},"\u002Fposts\u002F2020\u002Fopen-source-intelligence\u002Fosint-cover.webp",{},"\u002Fblog\u002F2020\u002Fopen-source-intelligence",{"title":5589,"description":13821},"blog\u002F2020\u002Fopen-source-intelligence","_z59ywl7_EfU9htOFFllPMPYV8EO5gGFttgEGTlT258",{"id":13830,"title":13831,"author":13832,"badge":13834,"body":13835,"date":14104,"description":14105,"draft":193,"extension":194,"image":14106,"meta":14108,"navigation":198,"path":14109,"seo":14110,"stem":14111,"__hash__":14112},"posts\u002Fblog\u002F2020\u002Fnew-design-for-the-blog.md","New design for the blog!",{"name":16,"to":17,"avatar":13833},{"src":19},{"label":21},{"type":23,"value":13836,"toc":14094},[13837,13840,13844,13847,13850,13854,13857,13865,13870,13876,13880,13887,13891,13898,13902,13909,13913,13927,13931,13940,13944,13947,13950,13954,13957,13972,13976,13979,13988,13997,14004,14008,14011,14014,14018,14021,14028,14042,14051,14053,14056,14091],[26,13838,13839],{},"I'm really excited for this! Don't ask me why because I can't explain it. This is not the first time I make a new design for this blog, it's actually the fifth time! and yet, I feel this is the best one yet. It has everything I ever wanted in my blog.",[458,13841,13843],{"id":13842},"_1-new-design","1. New Design",[26,13845,13846],{},"I can't even start to describe to you how much I feel this design is perfect for this blog. It has all the features I wanted: automatic categories and tags, content section in the posts, a comments system that doesn't sell your privacy and take forever to load (yes disqus I'm looking at you!), and dark mode! Ohh dark mode (It's not for everyone, I know, that's why it's optional).",[26,13848,13849],{},"Let's break it down",[4760,13851,13853],{"id":13852},"_11-navigation","1.1 Navigation",[26,13855,13856],{},"The navigation at the upper right side of the website contains all the configuration and links of the website.",[26,13858,13859],{},[41,13860],{"alt":13861,"className":13862,"src":13863,"height":3889,"width":13864},"Navigation",[45,46],"\u002Fposts\u002F2020\u002Fnew-design-blog\u002Fnavigation.webp",423,[13866,13867,13869],"h4",{"id":13868},"_111-posts","1.1.1 Posts",[26,13871,7635,13872,13875],{},[871,13873,13874],{},"Posts"," page will have all of the posts of the blog, it'll be ordered by year and will show the title of the post and the date it was published. This is the simplest way to go over old posts and just browse them.",[13866,13877,13879],{"id":13878},"_112-tags","1.1.2 Tags",[26,13881,13882,13883,13886],{},"Every post will have a list of tags, basically the main topics the post is about. The ",[871,13884,13885],{},"Tags"," page will host all of those tags. This way you can glance on all of the topics I write about and click on one of them and get a list of the posts that have that tag. This is really a great and easy way to search for a post topic.",[13866,13888,13890],{"id":13889},"_113-categories","1.1.3 Categories",[26,13892,13893,13894,13897],{},"In contrast to the Tags page the ",[871,13895,13896],{},"Categories"," page contains a list of all the posts divided by categories that I set in advance, each post can be in only one category so this is me trying to categorize all the posts by topics that I think are related and I want to explore (or maybe even write a series of posts about a single thing).",[13866,13899,13901],{"id":13900},"_114-about","1.1.4 About",[26,13903,7635,13904,13908],{},[58,13905,13907],{"href":13906},"\u002Fabout","About"," page is a simple one, it's just one static page with some information about me and the history of the blog. If you want to contact me, there is couple of ways to do that and it's all in there.",[13866,13910,13912],{"id":13911},"_115-github-link","1.1.5 GitHub Link",[26,13914,13915,13920,13921,13926],{},[58,13916,13919],{"href":13917,"rel":13918},"https:\u002F\u002Fgithub.com\u002Fnirgn975",[62],"GitHub"," is the main platform I'm on right now (and ",[58,13922,13925],{"href":13923,"rel":13924},"https:\u002F\u002Ftwitter.com\u002Fnirgn975",[62],"twitter","), it's the most important one because it stores all of my projects (work and private ones), my code examples, etc. And it makes sense to expect that I will direct readers there during the posts, so it's handy to have a direct link.",[13866,13928,13930],{"id":13929},"_116-search","1.1.6 Search",[26,13932,13933,13934,13939],{},"In time our blog will have a lot of posts. This will create a problem for new people who just came across this blog and went to search for a specific post related to a specific subject. To solve this problem I use ",[58,13935,13938],{"href":13936,"rel":13937},"https:\u002F\u002Fwww.algolia.com\u002F",[62],"algolia"," to provide a web search through a SaaS model. That way I keep the blog lean and static like I want to (I can focus on creating content and not features) while providing superhero powers!",[13866,13941,13943],{"id":13942},"_117-dark-mode","1.1.7 Dark Mode!",[26,13945,13946],{},"People that know me, know how much I love dark mode! I can't explain it, it's just easier on my eyes. I loved the old design of the blog, but not having a dark mode was a major bug. So the new design solved this by implementing a tiny button on the top right corner to toggle between light and dark modes.",[26,13948,13949],{},"This was a big motivation boost for me to move on from the old design. The toggle doesn't just add dark mode, it enables the option to choose for each user what is better for him\u002Fher, which is super important to me.",[4760,13951,13953],{"id":13952},"_12-post","1.2 Post",[26,13955,13956],{},"In the beginning of the old design I just had a title, text, pictures and code blocks. After some time I added the publish date of the post and how much time (approximately) it'll take to read the post (based on the number of words). In this new design we keep those features but add a content table to the right of the post, the category the post belongs to, the tags list at the bottom of the post, and much needed share buttons for easy sharing via social media.",[26,13958,13959,13960,13965,13966,13971],{},"On the technical side of the post I added a few features that will help me write better readable text, for example ",[58,13961,13964],{"href":13962,"rel":13963},"https:\u002F\u002Fgithub.com\u002FKaTeX\u002FKaTeX",[62],"KaTeX"," to render TeX math on the screen and ",[58,13967,13970],{"href":13968,"rel":13969},"https:\u002F\u002Fgithub.com\u002Fmermaid-js\u002Fmermaid",[62],"mermaid"," to generate diagrams and flowcharts.",[4760,13973,13975],{"id":13974},"_13-comments","1.3 Comments",[26,13977,13978],{},"When I moved from blogger and then tried Wordpress for a bit, I had a comment system in place. But when I moved to static sites for a while I didn't have any comment system and I was missing that. Missing the interactions and the users that tried for themselves the things I wrote about and got some errors and needed help. I know I need help sometimes when the documentation of a project isn't clear enough.",[26,13980,13981,13982,13987],{},"So after a while I implemented ",[58,13983,13986],{"href":13984,"rel":13985},"https:\u002F\u002Fdisqus.com\u002F",[62],"disqus"," but it was out of necessity. I didn't like it because it has a lot of privacy issues, it was really slow and the user couldn't make an anonymous comment (they force you to login via one of the social networks login buttons or sign up and open an account in their website).",[26,13989,13990,13991,13996],{},"A new design made a new opportunity to change all of that! I searched for days for the best commenting system for static websites that will keep the user's privacy, let the user make an anonymous comment, and will be beautiful and easy to use. And I think I found it! ",[58,13992,13995],{"href":13993,"rel":13994},"https:\u002F\u002Fwww.commento.io",[62],"commento"," is (in my research) the best there is out there right now, it fulfills all my requirements and more.",[26,13998,13999],{},[41,14000],{"alt":14001,"className":14002,"src":14003},"commento.io platform",[45,46],"\u002Fposts\u002F2020\u002Fnew-design-blog\u002Fcommento-io-platform.webp",[458,14005,14007],{"id":14006},"_2-new-language","2. New language",[26,14009,14010],{},"The hardest decision I had to make is to move to English or stay in Hebrew. The user base in Hebrew is very small, but there is something special and important in making quality content available to all (and by all I mean people who don't know English as a second language). And I did do that for years. But it's time to grow, my perspective has changed and I now think it's a must to at least be comfortable to read in English if you want to be successful in tech. I want to believe this will not be an issue to my Israeli readers and from now I'll start writing in English (I think it has a big part of my excitement).",[26,14012,14013],{},"English is not my native language (Hebrew is) so don't expect perfect. But I think I'm good enough to make it readable and I'm sure it'll get better over time, so bear with me.",[458,14015,14017],{"id":14016},"_3-analytics","3. Analytics",[26,14019,14020],{},"One of the things I highly value is the user's privacy, as I said before. But I need to know some stats about the blog (from where my readers are, views count, stats over time, etc). So, naturally I go to Google Analytics, but Google adds ad tracks to their script to keep tabs on my readers across the web, and that's something I can't accept. So, I search for an alternative.",[26,14022,14023],{},[41,14024],{"alt":14025,"className":14026,"src":14027},"Plausible Analytics elevator pitch",[45,46],"\u002Fposts\u002F2020\u002Fnew-design-blog\u002Fplausible-analytics-elevator-pitch.webp",[26,14029,14030,14031,14035,14036,14041],{},"And I found it, ",[58,14032,14034],{"href":4643,"rel":14033},[62],"Plausible Analytics"," is simple and privacy focused, exactly what I was searching for. And to check they're true to their words I use ",[58,14037,14040],{"href":14038,"rel":14039},"https:\u002F\u002Fthemarkup.org\u002Fblacklight\u002F?url=lifelongstudent.io",[62],"blacklight"," - A real-time website privacy inspector.",[26,14043,14044],{},[41,14045],{"alt":14046,"className":14047,"src":14048,"height":14049,"width":14050},"Blacklight search results",[45,46],"\u002Fposts\u002F2020\u002Fnew-design-blog\u002Fblacklight-search-results.webp",1225,646,[458,14052,767],{"id":766},[26,14054,14055],{},"So the new design of the blog is beautiful, useful, and I'm very excited to move to writing posts in English.",[26,14057,14058,14059,14062,14063,14067,14068,14073,14074,14079,14080,120,14085,14090],{},"I have a lot of great ideas for new posts and I can't wait to start writing them. Here is a little glimpse for one of them: a series of posts to build a little Twitter clone website with a live GitHub repo that will update at any post (so you could jump and ",[249,14060,14061],{},"checkout"," to the relevant post if you want), and in production level (deploy it to the ",[58,14064,14066],{"href":5602,"rel":14065},[62],"Google cloud",", monitor, backups, etc). We'll plan our architecture together, write a list of features (based on the real Twitter), build the client (",[58,14069,14072],{"href":14070,"rel":14071},"https:\u002F\u002Fangular.io\u002F",[62],"Angular",") and the server (",[58,14075,14078],{"href":14076,"rel":14077},"https:\u002F\u002Fnodejs.org",[62],"Node.js","), write tests, build CI\u002FCD with GitHub actions and GCP tools, and use ",[58,14081,14084],{"href":14082,"rel":14083},"https:\u002F\u002Fwww.docker.com\u002F",[62],"Docker",[58,14086,14089],{"href":14087,"rel":14088},"https:\u002F\u002Fkubernetes.io\u002F",[62],"kubernetes"," for deployment at scale. All of that step by step, it'll be fun!",[26,14092,14093],{},"There is a lot to wait for! (;",{"title":188,"searchDepth":189,"depth":189,"links":14095},[14096,14101,14102,14103],{"id":13842,"depth":189,"text":13843,"children":14097},[14098,14099,14100],{"id":13852,"depth":326,"text":13853},{"id":13952,"depth":326,"text":13953},{"id":13974,"depth":326,"text":13975},{"id":14006,"depth":189,"text":14007},{"id":14016,"depth":189,"text":14017},{"id":766,"depth":189,"text":767},"2020-04-25","Unveiling the blog's fifth redesign, with dark mode and privacy-friendly tooling.",{"src":14107},"\u002Fposts\u002F2020\u002Fnew-design-blog\u002Fblog-hero.webp",{},"\u002Fblog\u002F2020\u002Fnew-design-for-the-blog",{"title":13831,"description":14105},"blog\u002F2020\u002Fnew-design-for-the-blog","K_N6XrpJbiI47quApEEwOkTaq5y90PH3in3U1pzxWrQ",{"id":14114,"title":14115,"author":14116,"badge":14118,"body":14119,"date":14260,"description":14261,"draft":193,"extension":194,"image":14262,"meta":14264,"navigation":198,"path":14265,"seo":14266,"stem":14267,"__hash__":14268},"posts\u002Fblog\u002F2017\u002Fjekyll-starter-kit-generator-2.1.0-is-out.md","Jekyll Starter Kit generator 2.1.0 is out!",{"name":16,"to":17,"avatar":14117},{"src":19},{"label":4528},{"type":23,"value":14120,"toc":14255},[14121,14124,14128,14138,14147,14150,14199,14208,14223,14227,14238,14240],[26,14122,14123],{},"Creating Jekyll progressive web apps has never been easier!",[458,14125,14127],{"id":14126},"_1-jekyll-starter-kit","1. Jekyll Starter Kit",[26,14129,14130,14131,7292,14134,14137],{},"First thing first, for those of you who has yet to hear about the Jekyll Starter Kit generator, it’s a ",[58,14132,7291],{"href":7289,"rel":14133},[62],[58,14135,4547],{"href":7295,"rel":14136},[62]," projects or really Jekyll static websites.",[26,14139,14140,14141,14146],{},"It’ll create for you the default Jekyll website template, with all the best practices from ",[58,14142,14145],{"href":14143,"rel":14144},"https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fweb-starter-kit",[62],"Google Web Starter Kit",", all the things you need for your website to be a PWA (progressive web app), and some more cool stuff.",[26,14148,14149],{},"What more cool stuff? Here are couple of examples.",[151,14151,14152,14161,14164,14167,14175,14178,14181,14184,14193,14196],{},[154,14153,14154,14155,14160],{},"You can write ",[58,14156,14159],{"href":14157,"rel":14158},"https:\u002F\u002Fgithub.com\u002Fpugjs\u002Fpug",[62],"pug"," instead of HTML.",[154,14162,14163],{},"You can use CSS or SASS or SCSS.",[154,14165,14166],{},"Automagically minifies HTML, and automagically autoprefixing CSS.",[154,14168,14169,14170,126],{},"You can choose to write ES2015 with ",[58,14171,14174],{"href":14172,"rel":14173},"https:\u002F\u002Fgithub.com\u002Fbabel\u002Fbabel",[62],"babel",[154,14176,14177],{},"Concatenate and minify JavaScript.",[154,14179,14180],{},"Built-in preview and auto update with BrowserSync.",[154,14182,14183],{},"Automagically generates a service worker for your website for offline support.",[154,14185,14186,14187,14192],{},"Test the website against ",[58,14188,14191],{"href":14189,"rel":14190},"https:\u002F\u002Fgithub.com\u002FGoogleChrome\u002Flighthouse",[62],"lighthouse"," and fail Travis-CI if the score is below 80.",[154,14194,14195],{},"Automagically optimizes image before deploy.",[154,14197,14198],{},"Deploy the website to gh-pages or firebase with only one command.",[26,14200,14201],{},[41,14202],{"alt":14203,"className":14204,"height":14205,"src":14206,"width":14207},"The generator in action",[45,46],546,"\u002Fposts\u002F2017\u002Fjekyll-starter-kit-generator\u002Fthe-generator-in-action.webp",800,[26,14209,14210,14211,14214,14215,14218,14219,126],{},"If you can’t wait and already want to try it, all you need to do is ",[249,14212,14213],{},"npm install -g generator-jekyll-starter-kit"," and then just ",[249,14216,14217],{},"yo jekyll-starter-kit",". And if you want to help improve this project, just head up to the ",[58,14220,14222],{"href":7341,"rel":14221},[62],"repo page on GitHub",[458,14224,14226],{"id":14225},"_2-whats-new-in-version-210","2. What’s new in version 2.1.0?",[26,14228,14229,14230,14234,14235,14237],{},"Not much actually. The project is pretty stable with more than 1k uses (through ",[58,14231,1077],{"href":14232,"rel":14233},"https:\u002F\u002Fwww.npmjs.com\u002F",[62]," statistics). But some of the things we do for version 2.1 (from 2.0) is update dependencies, fix a small bug when the website output directory won't be removed before building the project, fix a small bug when deploy to gh-pages, switch from coverall to codecov (by the way the project has 100% coverage!), edit output ",[249,14236,7401],{}," file with more clear instructions, and add default favicons.",[458,14239,4839],{"id":4838},[26,14241,14242,14243,14248,14249,14254],{},"If you try to use it and need some help or just want to talk with the community (or with me), we have a ",[58,14244,14247],{"href":14245,"rel":14246},"https:\u002F\u002Fgitter.im\u002Fjekyll_starter_kit\u002FLobby",[62],"gitter chat room",". And if you would like to help make this generator better, you’re awesome! Go to the ",[58,14250,14253],{"href":14251,"rel":14252},"https:\u002F\u002Fgithub.com\u002Fnirgn975\u002Fgenerator-jekyll-starter-kit#want-to-help",[62],"Want to help"," section in the README and start from there.",{"title":188,"searchDepth":189,"depth":189,"links":14256},[14257,14258,14259],{"id":14126,"depth":189,"text":14127},{"id":14225,"depth":189,"text":14226},{"id":4838,"depth":189,"text":4839},"2017-08-03","What's new in version 2.1.0 of the Jekyll Starter Kit generator.",{"src":14263},"\u002Fposts\u002F2017\u002Fjekyll-starter-kit-generator\u002Fjekyll-starter-kit.webp",{},"\u002Fblog\u002F2017\u002Fjekyll-starter-kit-generator-2.1.0-is-out",{"title":14115,"description":14261},"blog\u002F2017\u002Fjekyll-starter-kit-generator-2.1.0-is-out","N17TDfqD8Pw0Dx48OMJi8dkATIICMyxNhnHiXqzxDAo",{"id":14270,"title":14271,"author":14272,"badge":14274,"body":14275,"date":15038,"description":15039,"draft":193,"extension":194,"image":15040,"meta":15042,"navigation":198,"path":15043,"seo":15044,"stem":15045,"__hash__":15046},"posts\u002Fblog\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker.md","Load Balancing Applications with HAProxy and Docker",{"name":16,"to":17,"avatar":14273},{"src":19},{"label":796},{"type":23,"value":14276,"toc":15030},[14277,14280,14288,14291,14302,14305,14309,14316,14361,14367,14397,14407,14410,14414,14433,14743,14746,14804,14811,14815,14842,14846,14853,14881,14888,14897,14904,14911,14917,14955,14990,14996,15004,15015,15017,15024,15027],[26,14278,14279],{},"A tutorial for a real world docker use case.",[26,14281,14282,14283,14287],{},"Recently I read a lot of articles about load balancing applications with ",[58,14284,14084],{"href":14285,"rel":14286},"https:\u002F\u002Fwww.docker.com",[62],", Docker Compose, and Docker Swarm for my work. We have a couple of hundreds of instances and we need to manage them and do load balancing between them.",[26,14289,14290],{},"There are a lot of articles about this topic, but sadly the use case they present is quite simple and because of that they really don’t help. Here is a couple of examples to what I mean:",[866,14292,14293,14296,14299],{},[154,14294,14295],{},"Manually create hundreds of containers is not practical.",[154,14297,14298],{},"Creating hundreds of containers each one on a different port is not practical.",[154,14300,14301],{},"Write the containers ip and port manually in nginx conf file is not practical.",[26,14303,14304],{},"For that reason I decided to write this post and present the way we use. It’s not by any mean the “right” way or the only way, but it’s the way that works for us right now. Also, I’m assuming you know Docker, Docker Compose, and Docker Swarm.",[458,14306,14308],{"id":14307},"_1-our-simple-application","1. Our simple application",[26,14310,14311,14312,14315],{},"Let’s start by creating our simple Node.js application. Create a file named ",[249,14313,14314],{},"index.js"," with the following code:",[242,14317,14319],{"className":244,"code":14318,"filename":14314,"language":247,"meta":188,"style":188},"var http = require(\"http\");\nvar os = require(\"os\");\nhttp\n  .createServer(function (req, res) {\n    res.writeHead(200, { \"Content-Type\": \"text\u002Fhtml\" });\n    res.end(`\u003Ch1>I'm ${os.hostname()}\u003C\u002Fh1>`);\n  })\n  .listen(8080);\n",[249,14320,14321,14326,14331,14336,14341,14346,14351,14356],{"__ignoreMap":188},[252,14322,14323],{"class":254,"line":255},[252,14324,14325],{},"var http = require(\"http\");\n",[252,14327,14328],{"class":254,"line":189},[252,14329,14330],{},"var os = require(\"os\");\n",[252,14332,14333],{"class":254,"line":326},[252,14334,14335],{},"http\n",[252,14337,14338],{"class":254,"line":343},[252,14339,14340],{},"  .createServer(function (req, res) {\n",[252,14342,14343],{"class":254,"line":354},[252,14344,14345],{},"    res.writeHead(200, { \"Content-Type\": \"text\u002Fhtml\" });\n",[252,14347,14348],{"class":254,"line":364},[252,14349,14350],{},"    res.end(`\u003Ch1>I'm ${os.hostname()}\u003C\u002Fh1>`);\n",[252,14352,14353],{"class":254,"line":381},[252,14354,14355],{},"  })\n",[252,14357,14358],{"class":254,"line":398},[252,14359,14360],{},"  .listen(8080);\n",[26,14362,14363,14364,14315],{},"Now we need to dockerize the app, so we’ll create a file named ",[249,14365,14366],{},"Dockerfile",[242,14368,14370],{"className":3526,"code":14369,"filename":14366,"language":3529,"meta":188,"style":188},"FROM node\nRUN mkdir -p \u002Fusr\u002Fsrc\u002Fapp\nCOPY index.js \u002Fusr\u002Fsrc\u002Fapp\nEXPOSE 8080\nCMD [ \"node\", \"\u002Fusr\u002Fsrc\u002Fapp\u002Findex\" ]\n",[249,14371,14372,14377,14382,14387,14392],{"__ignoreMap":188},[252,14373,14374],{"class":254,"line":255},[252,14375,14376],{"class":900},"FROM node\n",[252,14378,14379],{"class":254,"line":189},[252,14380,14381],{"class":900},"RUN mkdir -p \u002Fusr\u002Fsrc\u002Fapp\n",[252,14383,14384],{"class":254,"line":326},[252,14385,14386],{"class":900},"COPY index.js \u002Fusr\u002Fsrc\u002Fapp\n",[252,14388,14389],{"class":254,"line":343},[252,14390,14391],{"class":900},"EXPOSE 8080\n",[252,14393,14394],{"class":254,"line":354},[252,14395,14396],{"class":900},"CMD [ \"node\", \"\u002Fusr\u002Fsrc\u002Fapp\u002Findex\" ]\n",[26,14398,14399,14400,14403,14404,14406],{},"To build a docker image of our newly awesome Node.js app from this docker file instructions we’ll write ",[249,14401,14402],{},"docker build -t awesome ."," in the command line, where our ",[249,14405,14366],{}," file is located.",[26,14408,14409],{},"Now we have a docker image of our simple (and awesome) Node.js app, and we can create containers from that image. Let’s say we need 20 containers of that app, so we need an automated way to create those containers and manage them. Also we need a container with some HTTP server to route and load balance the requests to our Node.js containers.",[458,14411,14413],{"id":14412},"_2-enter-docker-compose","2. Enter Docker Compose",[26,14415,14416,14417,14422,14423,14428,14429,14432],{},"For our HTTP server we’ll use ",[58,14418,14421],{"href":14419,"rel":14420},"http:\u002F\u002Fwww.haproxy.org",[62],"HAProxy",", that means we need to create a container with HAProxy that will listen to port 80 and load balance the requests to the different Node.js containers on port 8080. To create our containers (Node.js apps and HAProxy) we’ll use ",[58,14424,14427],{"href":14425,"rel":14426},"https:\u002F\u002Fdocs.docker.com\u002Fcompose",[62],"Docker Compose",", let’s write our ",[249,14430,14431],{},"docker-compose.yml"," file:",[242,14434,14436],{"className":3526,"code":14435,"filename":14431,"language":3529,"meta":188,"style":188},"version: \"3\"\n\nservices:\n  awesome:\n    image: awesome\n    ports:\n      - 8080\n    environment:\n      - SERVICE_PORTS=8080\n    deploy:\n      replicas: 20\n      update_config:\n        parallelism: 5\n        delay: 10s\n      restart_policy:\n        condition: on-failure\n        max_attempts: 3\n        window: 120s\n    networks:\n      - web\n\n  proxy:\n    image: dockercloud\u002Fhaproxy\n    depends_on:\n      - awesome\n    environment:\n      - BALANCE=leastconn\n    volumes:\n      - \u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock\n    ports:\n      - 80:80\n    networks:\n      - web\n    deploy:\n      placement:\n        constraints: [node.role == manager]\n\nnetworks:\n  web:\n    driver: overlay\n",[249,14437,14438,14452,14456,14463,14470,14480,14487,14494,14501,14508,14515,14525,14532,14542,14552,14559,14569,14579,14589,14596,14603,14607,14614,14623,14630,14636,14642,14649,14656,14663,14669,14676,14682,14688,14694,14701,14715,14719,14726,14733],{"__ignoreMap":188},[252,14439,14440,14443,14445,14447,14450],{"class":254,"line":255},[252,14441,14442],{"class":308},"version",[252,14444,312],{"class":262},[252,14446,1471],{"class":262},[252,14448,14449],{"class":900},"3",[252,14451,3062],{"class":262},[252,14453,14454],{"class":254,"line":189},[252,14455,1695],{"emptyLinePlaceholder":198},[252,14457,14458,14461],{"class":254,"line":326},[252,14459,14460],{"class":308},"services",[252,14462,3553],{"class":262},[252,14464,14465,14468],{"class":254,"line":343},[252,14466,14467],{"class":308},"  awesome",[252,14469,3553],{"class":262},[252,14471,14472,14475,14477],{"class":254,"line":354},[252,14473,14474],{"class":308},"    image",[252,14476,312],{"class":262},[252,14478,14479],{"class":900}," awesome\n",[252,14481,14482,14485],{"class":254,"line":364},[252,14483,14484],{"class":308},"    ports",[252,14486,3553],{"class":262},[252,14488,14489,14491],{"class":254,"line":381},[252,14490,3572],{"class":262},[252,14492,14493],{"class":1782}," 8080\n",[252,14495,14496,14499],{"class":254,"line":398},[252,14497,14498],{"class":308},"    environment",[252,14500,3553],{"class":262},[252,14502,14503,14505],{"class":254,"line":415},[252,14504,3572],{"class":262},[252,14506,14507],{"class":900}," SERVICE_PORTS=8080\n",[252,14509,14510,14513],{"class":254,"line":421},[252,14511,14512],{"class":308},"    deploy",[252,14514,3553],{"class":262},[252,14516,14517,14520,14522],{"class":254,"line":427},[252,14518,14519],{"class":308},"      replicas",[252,14521,312],{"class":262},[252,14523,14524],{"class":1782}," 20\n",[252,14526,14527,14530],{"class":254,"line":662},[252,14528,14529],{"class":308},"      update_config",[252,14531,3553],{"class":262},[252,14533,14534,14537,14539],{"class":254,"line":668},[252,14535,14536],{"class":308},"        parallelism",[252,14538,312],{"class":262},[252,14540,14541],{"class":1782}," 5\n",[252,14543,14544,14547,14549],{"class":254,"line":674},[252,14545,14546],{"class":308},"        delay",[252,14548,312],{"class":262},[252,14550,14551],{"class":900}," 10s\n",[252,14553,14554,14557],{"class":254,"line":1414},[252,14555,14556],{"class":308},"      restart_policy",[252,14558,3553],{"class":262},[252,14560,14561,14564,14566],{"class":254,"line":1610},[252,14562,14563],{"class":308},"        condition",[252,14565,312],{"class":262},[252,14567,14568],{"class":900}," on-failure\n",[252,14570,14571,14574,14576],{"class":254,"line":1618},[252,14572,14573],{"class":308},"        max_attempts",[252,14575,312],{"class":262},[252,14577,14578],{"class":1782}," 3\n",[252,14580,14581,14584,14586],{"class":254,"line":1628},[252,14582,14583],{"class":308},"        window",[252,14585,312],{"class":262},[252,14587,14588],{"class":900}," 120s\n",[252,14590,14591,14594],{"class":254,"line":1645},[252,14592,14593],{"class":308},"    networks",[252,14595,3553],{"class":262},[252,14597,14598,14600],{"class":254,"line":1655},[252,14599,3572],{"class":262},[252,14601,14602],{"class":900}," web\n",[252,14604,14605],{"class":254,"line":1660},[252,14606,1695],{"emptyLinePlaceholder":198},[252,14608,14609,14612],{"class":254,"line":2363},[252,14610,14611],{"class":308},"  proxy",[252,14613,3553],{"class":262},[252,14615,14616,14618,14620],{"class":254,"line":2392},[252,14617,14474],{"class":308},[252,14619,312],{"class":262},[252,14621,14622],{"class":900}," dockercloud\u002Fhaproxy\n",[252,14624,14625,14628],{"class":254,"line":2407},[252,14626,14627],{"class":308},"    depends_on",[252,14629,3553],{"class":262},[252,14631,14632,14634],{"class":254,"line":2421},[252,14633,3572],{"class":262},[252,14635,14479],{"class":900},[252,14637,14638,14640],{"class":254,"line":2436},[252,14639,14498],{"class":308},[252,14641,3553],{"class":262},[252,14643,14644,14646],{"class":254,"line":2441},[252,14645,3572],{"class":262},[252,14647,14648],{"class":900}," BALANCE=leastconn\n",[252,14650,14651,14654],{"class":254,"line":2446},[252,14652,14653],{"class":308},"    volumes",[252,14655,3553],{"class":262},[252,14657,14658,14660],{"class":254,"line":2472},[252,14659,3572],{"class":262},[252,14661,14662],{"class":900}," \u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock\n",[252,14664,14665,14667],{"class":254,"line":2486},[252,14666,14484],{"class":308},[252,14668,3553],{"class":262},[252,14670,14671,14673],{"class":254,"line":2501},[252,14672,3572],{"class":262},[252,14674,14675],{"class":900}," 80:80\n",[252,14677,14678,14680],{"class":254,"line":3002},[252,14679,14593],{"class":308},[252,14681,3553],{"class":262},[252,14683,14684,14686],{"class":254,"line":3007},[252,14685,3572],{"class":262},[252,14687,14602],{"class":900},[252,14689,14690,14692],{"class":254,"line":3012},[252,14691,14512],{"class":308},[252,14693,3553],{"class":262},[252,14695,14696,14699],{"class":254,"line":3805},[252,14697,14698],{"class":308},"      placement",[252,14700,3553],{"class":262},[252,14702,14703,14706,14708,14710,14713],{"class":254,"line":3810},[252,14704,14705],{"class":308},"        constraints",[252,14707,312],{"class":262},[252,14709,1263],{"class":262},[252,14711,14712],{"class":900},"node.role == manager",[252,14714,3476],{"class":262},[252,14716,14717],{"class":254,"line":3822},[252,14718,1695],{"emptyLinePlaceholder":198},[252,14720,14721,14724],{"class":254,"line":3832},[252,14722,14723],{"class":308},"networks",[252,14725,3553],{"class":262},[252,14727,14728,14731],{"class":254,"line":3840},[252,14729,14730],{"class":308},"  web",[252,14732,3553],{"class":262},[252,14734,14735,14738,14740],{"class":254,"line":3851},[252,14736,14737],{"class":308},"    driver",[252,14739,312],{"class":262},[252,14741,14742],{"class":900}," overlay\n",[26,14744,14745],{},"Let’s explain what the hell is happening here. We create 2 services:",[866,14747,14748,14766,14799],{},[154,14749,14750,14751,14754,14755,14757,14758,14761,14762,14765],{},"The first service is our Node.js app (that call ",[249,14752,14753],{},"awesome","). It builds that service from an image that call ",[249,14756,14753],{}," (that we previously built). We expose port 8080, and add an environment variable named ",[249,14759,14760],{},"SERVICE_PORTS"," with the port we exposed (we do it for the HAProxy, I’ll explain why later on). We also create 20 replicas from that image (20 containers) and have some update and restart settings for them. The important thing to notice is that we put all those containers in a network called ",[249,14763,14764],{},"web"," (we create the network later, in the end of the file).",[154,14767,14768,14769,14771,14772,14774,14775,707,14778,14780,14781,14783,14784,14787,14788,14791,14792,14794,14795,14798],{},"The second service we create is ",[249,14770,14421],{}," from the haproxy Docker (the company) uses in their cloud (we don’t build it and doesn’t create a ",[249,14773,14366],{}," for it, we just specify from where to pull the image). It ",[249,14776,14777],{},"depends_on",[249,14779,14753],{}," service, so it won’t boot until the ",[249,14782,14753],{}," service is finished (i.e. all of the container are up and running). It’s also shared the ",[249,14785,14786],{},"docker.sock"," file (",[249,14789,14790],{},"volumes"," field). This is the file the HAProxy container needs to look at to learn about the containers in its network (new and existing containers). We expose port 80, and put this container in the ",[249,14793,14764],{}," network, In the ",[249,14796,14797],{},"deploy"," setting, we just tell it to always put this container on the manager node (these settings are related to Docker Swarm, when we have a couple of nodes (i.e. servers).",[154,14800,14801,14802,983],{},"The last thing we do is to create the network (named ",[249,14803,14764],{},[26,14805,14806],{},[41,14807],{"alt":14808,"className":14809,"src":14810},"Our project looks good so far, and we almost at the finish line!",[45,46],"\u002Fposts\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker\u002Four-project-looks-good-so-far.webp",[458,14812,14814],{"id":14813},"_3-dockercloud-haproxy","3. DockerCloud HAProxy",[26,14816,14817,14818,14823,14824,14826,14827,14829,14830,14833,14834,14837,14838,14841],{},"For our HTTP Server we use HAProxy, but not the regular version of HAProxy, we use the version ",[58,14819,14822],{"href":14820,"rel":14821},"https:\u002F\u002Fgithub.com\u002Fdocker\u002Fdockercloud-haproxy",[62],"docker uses in their cloud",". That’s why we set an environment variable named ",[249,14825,14760],{}," in our ",[249,14828,14753],{}," service, because this is the ports that will be exposed to HAProxy (we can put multiple ports there by separating them with a comma). We also set an environment variable named ",[249,14831,14832],{},"BALANCE"," to set the load balancing algorithm to be ",[249,14835,14836],{},"leastconn"," and not ",[249,14839,14840],{},"roundrobin"," (which is what's set by default).",[4760,14843,14845],{"id":14844},"_31-docker-swarm","3.1 Docker Swarm",[26,14847,14848,14849,14852],{},"Now let’s create a swarm (with one computer for now, but you can easily add more to the swarm). To do this we'll write ",[249,14850,14851],{},"docker swarm init"," and we created a swarm!! It’s also added our computer to the swarm, and because our computer is the first it’s also the manager of the swarm.",[26,14854,14855,14856,14859,14860,14863,14864,14866,14867,14870,14871,14873,14874,14876,14877,14880],{},"The network, the services, and all the containers called ",[249,14857,14858],{},"stack",". To create our stack we need to use ",[249,14861,14862],{},"docker stack"," command, but we want to point the stack to our ",[249,14865,14431],{}," file, so it’ll build the stuck according to our plan there. We can do it by writing ",[249,14868,14869],{},"docker stack deploy --compose-file=docker-compose.yml prod",". we use the ",[249,14872,14797],{}," command to deploy a new stack (we’ll also use it to update an existing stack), we add a flag to point it to our ",[249,14875,14431],{}," file. And finally we call our stack ",[249,14878,14879],{},"prod"," (this is what came up in my head when I wrote those lines, sorry).",[26,14882,14883,14884,14887],{},"When we’ll hit ",[249,14885,14886],{},"http:\u002F\u002Flocalhost"," we’ll get the container id in the response, and we can see it has a different id every request.",[26,14889,14890],{},[41,14891],{"alt":14892,"className":14893,"src":14894,"height":14895,"width":14896},"Different container id every request",[45,46],"\u002Fposts\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker\u002Fdifferent-container-id-evert-request.webp",195,171,[26,14898,14899,14900,14903],{},"Now let’s look at our services by writing ",[249,14901,14902],{},"docker service ls"," and we’ll see all of our services and replicas",[26,14905,14906],{},[41,14907],{"alt":14908,"className":14909,"src":14910,"height":9535,"width":14207},"All of our docker services",[45,46],"\u002Fposts\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker\u002Fall-of-our-docker-services.webp",[26,14912,14913,14914,14916],{},"We can also create a second version of our ",[249,14915,14753],{}," app. Let’s change the code a little bit (let’s add some exclamation marks at the end):",[242,14918,14920],{"className":244,"code":14919,"filename":14314,"language":247,"meta":188,"style":188},"var http = require(\"http\");\nvar os = require(\"os\");\nhttp\n  .createServer(function (req, res) {\n    res.writeHead(200, { \"Content-Type\": \"text\u002Fhtml\" });\n    res.end(`\u003Ch1>I'm ${os.hostname()}!!!\u003C\u002Fh1>`);\n  })\n  .listen(8080);\n",[249,14921,14922,14926,14930,14934,14938,14942,14947,14951],{"__ignoreMap":188},[252,14923,14924],{"class":254,"line":255},[252,14925,14325],{},[252,14927,14928],{"class":254,"line":189},[252,14929,14330],{},[252,14931,14932],{"class":254,"line":326},[252,14933,14335],{},[252,14935,14936],{"class":254,"line":343},[252,14937,14340],{},[252,14939,14940],{"class":254,"line":354},[252,14941,14345],{},[252,14943,14944],{"class":254,"line":364},[252,14945,14946],{},"    res.end(`\u003Ch1>I'm ${os.hostname()}!!!\u003C\u002Fh1>`);\n",[252,14948,14949],{"class":254,"line":381},[252,14950,14355],{},[252,14952,14953],{"class":254,"line":398},[252,14954,14360],{},[26,14956,14957,14958,14961,14962,14964,14965,14968,14969,14971,14972,14974,14975,14978,14979,14981,14982,14984,14985,14826,14988,4769],{},"So we need to build the image again, but this time it’s the second version of the app so we’ll write ",[249,14959,14960],{},"docker build -t awesome:v2 ."," and we’ll create an image called ",[249,14963,14753],{}," but with a ",[249,14966,14967],{},"v2"," tag. To update our containers in the ",[249,14970,14753],{}," service to use the ",[249,14973,14967],{}," version of our app (without stop the service) we’ll write ",[249,14976,14977],{},"docker service update --image awesome:v2 prod_awesome"," and our service called ",[249,14980,14753],{},", in ",[249,14983,14879],{}," stack, will update its containers five by five to use the second version of our app (why 5 containers at a time? because we wrote ",[249,14986,14987],{},"parallelism: 5",[249,14989,14431],{},[26,14991,14992,14993,14995],{},"We can see our docker slowly (but surely) kill the old containers and create new ones with the second version of our app. And when we hit ",[249,14994,14886],{}," we still get a response, there is no downtime.",[26,14997,14998],{},[41,14999],{"alt":15000,"className":15001,"src":15002,"height":15003,"width":7244},"Some containers are already use the second version. No Downtime (:",[45,46],"\u002Fposts\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker\u002Fsome-containers-are-already-use-the-second-version.webp",173,[26,15005,15006,15007,15010,15011,15014],{},"Also, if we want to scale the service to more than 20 containers, we can do it with only one command: ",[249,15008,15009],{},"docker service scale prod_awesome=50"," and docker will start 30 more containers from the ",[249,15012,15013],{},"awesome:v2"," image.",[458,15016,767],{"id":766},[26,15018,15019,15020,15023],{},"So, we don’t need to create hundreds of containers manually. We don’t need to place every container of our app in a different port. We don’t need to manually write our containers ip and port in nginx\u002Fhaproxy ",[249,15021,15022],{},"conf"," file. And we can do it with multiple servers (with docker swarm), with multiple services (with docker compose), update our application without downtime, and scale it up (or down) without downtime.",[26,15025,15026],{},"Hope it’s been practical, and I would love to hear how you do it in your company!",[772,15028,15029],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":188,"searchDepth":189,"depth":189,"links":15031},[15032,15033,15034,15037],{"id":14307,"depth":189,"text":14308},{"id":14412,"depth":189,"text":14413},{"id":14813,"depth":189,"text":14814,"children":15035},[15036],{"id":14844,"depth":326,"text":14845},{"id":766,"depth":189,"text":767},"2017-07-11","Load balancing containerized apps with Docker Compose, Docker Swarm, and HAProxy.",{"src":15041},"\u002Fposts\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker\u002Flets-start.webp",{},"\u002Fblog\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker",{"title":14271,"description":15039},"blog\u002F2017\u002Fload-balancing-applications-with-haproxy-and-docker","ky06CKpQCPXqRqO7qAtalPXewBR9X-PbvnUvx_KvnpY",{"id":15048,"title":15049,"author":15050,"badge":15052,"body":15054,"date":15266,"description":15267,"draft":193,"extension":194,"image":15268,"meta":15269,"navigation":198,"path":15270,"seo":15271,"stem":15272,"__hash__":15273},"posts\u002Fblog\u002F2012\u002Fupdate-zip.md","Create an update.zip file",{"name":16,"to":17,"avatar":15051},{"src":19},{"label":15053},"android",{"type":23,"value":15055,"toc":15262},[15056,15059,15067,15071,15076,15095,15100,15125,15163,15166,15238,15249,15253,15256,15259],[26,15057,15058],{},"I assume the most common and lighthearted example is the battery mod. For some reason in Android (don't ask me why, I don't know) the battery doesn't have percentages, only a drawing that gives us a (pretty rough) estimate of the battery's state (even though the system knows exactly how many percent it has, they're just not shown in the top bar). That's why the most popular file people flash is the \"battery with percentages\" mod (and sometimes a different battery drawing too).",[26,15060,15061,15062,15066],{},"Note that you can see in the post ",[58,15063,15065],{"href":15064},"\u002Fblog\u002F2012\u002Fanalyze-an-apk-file\u002F","Analyze an Apk file"," a bit more information about opening and modifying applications (apk files). In addition, I'll mention that this guide is general, you can put inside the zip file a boot animation, an apk we modified, a mod to change the battery display, basically anything. In this case I'll demonstrate with a BootAnimation file I created for myself.",[458,15068,15070],{"id":15069},"_1-how-do-you-build-a-zip-file-that-can-be-flashed-in-recovery","1. How do you build a zip file that can be flashed in recovery?",[26,15072,15073],{},[871,15074,15075],{},"Step one, preparations:",[151,15077,15078,15081,15089],{},[154,15079,15080],{},"The first thing we need is the file \u002F files we want to add to the Rom (in my case it's the file I prepared for myself: BootAnimation.zip).",[154,15082,15083,15084,126],{},"Now we need to download the Testsign.jar file which will help us sign the zip, and it can be downloaded ",[58,15085,15088],{"href":15086,"rel":15087},"https:\u002F\u002Fcode.google.com\u002Fp\u002Fzen-droid\u002Fdownloads\u002Fdetail?name=testsign.jar&can=2&q=label%3AType-Executable",[62],"from here",[154,15090,15091,15092,983],{},"And last, we need to decide where (to which path) the files we want to add will go (in my case to the path ",[249,15093,15094],{},"system\u002Fmedia\u002F",[26,15096,15097],{},[871,15098,15099],{},"Let's get to work:",[866,15101,15102,15105,15122],{},[154,15103,15104],{},"Open a folder with the name of the path it's going to. In this example we need to prepare a folder called media, and put the BootAnimation.zip file inside it. Note that it doesn't matter where we prepare the folder, you can open a new folder anywhere you'd like, even just on the desktop.",[154,15106,15107,15108,15111,15112,15117,15118,15121],{},"Now we need to prepare a few more folders, following the path ",[249,15109,15110],{},"META-INF\u002Fcom\u002Fgoogle\u002Fandroid",", and inside it open a new text file in ",[58,15113,15116],{"href":15114,"rel":15115},"http:\u002F\u002Fwww.sublimetext.com\u002F2",[62],"Sublime"," and name it ",[249,15119,15120],{},"update-script",". This is the path Recovery goes to look for the update-script file, and it will read this file for the instructions, or \"what to do\".",[154,15123,15124],{},"Open the file and copy into it:",[242,15126,15131],{"className":15127,"code":15128,"filename":15129,"language":15130,"meta":188,"style":188},"language-shell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","ui_print(\"Applying New Bootanimation\");\nrun_program(\"\u002Fsbin\u002Fbusybox\", \"mount\", \"\u002Fsystem\");\ndelete(\"\u002Fsystem\u002Fmedia\u002Fbootanimation.zip\");\npackage_extract_dir(\"media\", \"\u002Fsystem\u002Fmedia\");\nrun_program(\"\u002Fsbin\u002Fbusybox\", \"umount\", \"\u002Fsystem\");\nui_print(\"New Bootanimation Installed\");\n","update-script.txt","shell",[249,15132,15133,15138,15143,15148,15153,15158],{"__ignoreMap":188},[252,15134,15135],{"class":254,"line":255},[252,15136,15137],{},"ui_print(\"Applying New Bootanimation\");\n",[252,15139,15140],{"class":254,"line":189},[252,15141,15142],{},"run_program(\"\u002Fsbin\u002Fbusybox\", \"mount\", \"\u002Fsystem\");\n",[252,15144,15145],{"class":254,"line":326},[252,15146,15147],{},"delete(\"\u002Fsystem\u002Fmedia\u002Fbootanimation.zip\");\n",[252,15149,15150],{"class":254,"line":343},[252,15151,15152],{},"package_extract_dir(\"media\", \"\u002Fsystem\u002Fmedia\");\n",[252,15154,15155],{"class":254,"line":354},[252,15156,15157],{},"run_program(\"\u002Fsbin\u002Fbusybox\", \"umount\", \"\u002Fsystem\");\n",[252,15159,15160],{"class":254,"line":364},[252,15161,15162],{},"ui_print(\"New Bootanimation Installed\");\n",[26,15164,15165],{},"And the last step is to understand what we wrote, for that we'll break the code down line by line and explain what's going on here:",[151,15167,15168,15177,15190,15202,15217,15229],{},[154,15169,15170,11763,15173,15176],{},[871,15171,15172],{},"The first line:",[249,15174,15175],{},"ui_print(\"Applying New Bootanimation\");"," prints what's inside the quotes (which are inside the parentheses). This line isn't essential, the file will work without it too, but it's more elegant, clean, and lets whoever installs the script understand what's happening right now.",[154,15178,15179,11763,15182,15185,15186,15189],{},[871,15180,15181],{},"The second line:",[249,15183,15184],{},"run_program(\"\u002Fsbin\u002Fbusybox\", \"mount\", \"\u002Fsystem\");"," runs busybox which makes the ",[249,15187,15188],{},"system\u002F"," folder writable (so we can write to it).",[154,15191,15192,11763,15195,15198,15199,15201],{},[871,15193,15194],{},"The third line:",[249,15196,15197],{},"delete(\"\u002Fsystem\u002Fmedia\u002Fbootanimation.zip\");"," deletes the bootAnimation.zip file located at the path ",[249,15200,15094],{}," (so we can write the new file).",[154,15203,15204,11763,15207,15210,15211,15214,15215,126],{},[871,15205,15206],{},"In the fourth line:",[249,15208,15209],{},"package_extract_dir(\"media\", \"\u002Fsystem\u002Fmedia\");"," we tell the device to find, inside our file (the Package), the ",[249,15212,15213],{},"media"," folder (inside the folder where we put the bootAnimation.zip file) and move its contents to the path ",[249,15216,15094],{},[154,15218,15219,11763,15222,15225,15226,15228],{},[871,15220,15221],{},"In the fifth line:",[249,15223,15224],{},"run_program(\"\u002Fsbin\u002Fbusybox\", \"umount\", \"\u002Fsystem\");"," we run busybox again and give it the umount command on the ",[249,15227,15188],{}," folder.",[154,15230,15231,11763,15234,15237],{},[871,15232,15233],{},"The sixth line:",[249,15235,15236],{},"ui_print(\"New Bootanimation Installed\");"," like the first line, prints a message to the user.",[26,15239,15240,15243,15244,126],{},[951,15241,15242],{},"Tip:"," A guy on XDA wrote a small program that can do this whole process for you. It's always better to know how everything is done and the lines of code, but this is a fairly technical operation, and the program definitely helps save time and simplify the process (especially for people who do this every day). In addition, the program has a few more options that can help. Here's the ",[58,15245,15248],{"href":15246,"rel":15247},"http:\u002F\u002Fforum.xda-developers.com\u002Fshowthread.php?t=1248486",[62],"link to the post on XDA",[458,15250,15252],{"id":15251},"_2-summary","2. Summary",[26,15254,15255],{},"In a few steps you've prepared a file that can be flashed through Recovery and in which you can put anything you want (new Apk files to flash, replacing existing apk files, various mods, add-ons, a new Rom, etc.).",[26,15257,15258],{},"Note, that when you're changing something existing, for example an Apk file, you need to delete the old file first, and only then copy the new Apk file (as I demonstrated in this case, the delete line comes before the copy line for the file). If it's new files you simply need to copy them and not write the delete line.",[772,15260,15261],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":188,"searchDepth":189,"depth":189,"links":15263},[15264,15265],{"id":15069,"depth":189,"text":15070},{"id":15251,"depth":189,"text":15252},"2012-12-16","Building a flashable update.zip file for Android Recovery in 5 steps.",{"src":7436},{},"\u002Fblog\u002F2012\u002Fupdate-zip",{"title":15049,"description":15267},"blog\u002F2012\u002Fupdate-zip","QoG06nYys8OTRTNr3_UdcRWc9EXUd8duV39Z9_uD1CM",{"id":15275,"title":15065,"author":15276,"badge":15278,"body":15279,"date":15585,"description":15586,"draft":193,"extension":194,"image":15587,"meta":15588,"navigation":198,"path":15589,"seo":15590,"stem":15591,"__hash__":15592},"posts\u002Fblog\u002F2012\u002Fanalyze-an-apk-file.md",{"name":16,"to":17,"avatar":15277},{"src":19},{"label":15053},{"type":23,"value":15280,"toc":15577},[15281,15284,15287,15299,15303,15311,15326,15336,15340,15366,15373,15377,15380,15383,15386,15403,15408,15489,15493,15496,15499,15506,15514,15520,15528,15534,15538,15541,15555,15558,15561,15572,15574],[26,15282,15283],{},"One of the useful (and cool) things about Android is knowing how to open and sign Apk files, and of course, as a result, being able to edit them.",[26,15285,15286],{},"As we know, Android is built entirely out of Apk files, so in this post we'll learn how to open them, make changes, and finally rebuild and re-sign them. I'll demonstrate on an Apk file of an app from Play, but of course you can do this with any application (including ones that come built into the system).",[26,15288,15289,15290,15294,15295,126],{},"First, in order to know what an Apk file is and how it's built, read the post ",[58,15291,15293],{"href":15292},"\u002Fblog\u002F2012\u002Flittle-android-bit-grammar\u002F","A little bit of Android Grammar",". In addition, you'll need to install the JDK and JRE from ",[58,15296,11767],{"href":15297,"rel":15298},"http:\u002F\u002Fwww.oracle.com\u002Ftechnetwork\u002Fjava\u002Fjavase\u002Fdownloads\u002Findex.html",[62],[458,15300,15302],{"id":15301},"_1-preparations","1. Preparations",[26,15304,15305,15306,983],{},"In order to open Apk files and later rebuild them, we'll use the ApkTool software (download the file ",[58,15307,15310],{"href":15308,"rel":15309},"https:\u002F\u002Fbitbucket.org\u002FiBotPeaches\u002Fapktool\u002Fdownloads",[62],"apktool1.5.2.tar.bz2",[26,15312,15313,15314,15319,15320,15325],{},"In addition, the file ",[58,15315,15318],{"href":15316,"rel":15317},"https:\u002F\u002Fraw.githubusercontent.com\u002FiBotPeaches\u002FApktool\u002Fmaster\u002Fscripts\u002Fwindows\u002Fapktool.bat",[62],"apktool-install-windows-r05-ibot.tar.bz2"," (if you're using Windows), or the file ",[58,15321,15324],{"href":15322,"rel":15323},"https:\u002F\u002Fraw.githubusercontent.com\u002FiBotPeaches\u002FApktool\u002Fmaster\u002Fscripts\u002Flinux\u002Fapktool",[62],"apktool-install-linux-r05-ibot.tar.bz2"," (if you're using Linux).",[26,15327,15328,15329,15332,15333,126],{},"Then, extract the Jar file found inside the folder from the first file we downloaded (apktool1.5.2.tar.bz2) into the folder that came from the second file (the Windows or Linux one). Note that you can place this folder wherever is convenient for you, I recommend the path ",[249,15330,15331],{},"usr\u002Flocal\u002Fbin\u002F"," on Linux, or on Windows the path ",[249,15334,15335],{},"C:\\Program Files\\Android\\android-sdk\\platform-tools",[458,15337,15339],{"id":15338},"_2-decompile","2. Decompile",[866,15341,15342,15345,15348],{},[154,15343,15344],{},"Move the application (the Apk file) to the apktool folder.",[154,15346,15347],{},"Open the Terminal (or Cmd), navigate to the location where you placed the apktool folder.",[154,15349,15350,15351,15354,15355],{},"To open \u002F break apart the file we write ",[249,15352,15353],{},"apktool d namOfTheApp.apk nameOfTheFolder",".\n",[151,15356,15357,15360,15363],{},[154,15358,15359],{},"Replace namOfTheApp with the file's name, and replace nameOfTheFolder with the name you want to give the folder (it'll be created inside the apktool folder).",[154,15361,15362],{},"On Linux add \u002F. before the command.",[154,15364,15365],{},"The letter d that appears after apktool in the command is short for decompile.",[26,15367,15368,15369,15372],{},"Example (on Linux): if the name of the file we want to break apart is HelloWorld.apk, we write: ",[249,15370,15371],{},"apktool d HelloWorld.apk appOne\u002F.",", and a folder named appOne will be created (inside the apktool folder).",[458,15374,15376],{"id":15375},"_3-build","3. Build",[26,15378,15379],{},"To rebuild the application, we're required (as a last step) to sign it. Every application must be digitally signed, the signing key is private and held by the app's developer.",[26,15381,15382],{},"The signature, in other words the digital certificate, provides the Android system with a way to identify the app's developer, and to establish trusted relationships between applications.",[26,15384,15385],{},"The important points to understand about signatures are:",[151,15387,15388,15391,15394,15397,15400],{},[154,15389,15390],{},"All applications must be signed. The system will not install unsigned applications on the device, or on the emulator.",[154,15392,15393],{},"In order to test and debug applications developers are building, the Android SDK signs them automatically with a generic generated key, meant specifically for debugging.",[154,15395,15396],{},"When the developer releases the version to Play, it must be signed with a private key, an application can't be released while signed with the key the Android SDK generates.",[154,15398,15399],{},"Every certificate has an expiration date, once that date has passed the system won't install applications signed with that certificate. However, note that the system only checks this date at install time, so if the date has passed after the app was already installed, the app will keep functioning normally.",[154,15401,15402],{},"You can use standard tools (such as Keytool and Jarsigner) to generate a key to sign the application with.",[26,15404,15405],{},[871,15406,15407],{},"Let's start building:",[866,15409,15410,15429,15469],{},[154,15411,15412,15413,15354,15416],{},"To build the application, we write the following command ",[249,15414,15415],{},"apktool b nameOfTheFolder nameOfTheApp.apk",[151,15417,15418,15421,15424,15426],{},[154,15419,15420],{},"The command should, of course, be written in the terminal (or Cmd) while inside the apktool folder's path.",[154,15422,15423],{},"Replace nameOfTheFolder with the name of the folder containing the files, and replace nameOfTheApp with the name you want to give the application.",[154,15425,15362],{},[154,15427,15428],{},"The letter b that appears after apktool in the command is short for build (or recompile).",[154,15430,15431,15432,15354,15435],{},"In order to generate a key we can use to sign the apk file, you need to install the Android SDK. Then open the terminal (or Cmd) and write ",[249,15433,15434],{},"keytool -v -genkey -v -keystore nameOfKey.keystore -alias aliasName -keyalg RSA -validity 10000",[151,15436,15437,15440,15443,15446,15449,15463,15466],{},[154,15438,15439],{},"genkey is short for generate key.",[154,15441,15442],{},"v is short for verbose mode.",[154,15444,15445],{},"nameOfKey will be the key's name (you can replace it with whatever you'd like).",[154,15447,15448],{},"aliasName creates an alias (another name) for the key (you can replace it with whatever you'd like).",[154,15450,15451,15452,64,15457,15462],{},"keyalg specifies the encryption algorithm used to generate the key (i.e. ",[58,15453,15456],{"href":15454,"rel":15455},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FRSA_(cryptosystem)",[62],"RSA",[58,15458,15461],{"href":15459,"rel":15460},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FDigital_Signature_Algorithm",[62],"DSA",", etc.).",[154,15464,15465],{},"validity describes, in days, how long the key is valid for (in this case I wrote 10,000 days).",[154,15467,15468],{},"As part of generating the key you'll need to fill in personal details, and choose a password for the key.",[154,15470,15471,15472,15354,15475],{},"Now let's sign the application, and to shorten the command, move the apk file and the key to the same folder, and write in the terminal (or Cmd) the command ",[249,15473,15474],{},"jarsigner -verbose -keystore myKeyName.keystore appName.apk aliasName",[151,15476,15477,15480,15483,15486],{},[154,15478,15479],{},"myKeyName is the name of the key we created in the section above.",[154,15481,15482],{},"appName is the name of the apk file.",[154,15484,15485],{},"aliasName is the alias of the key you wrote in the section above.",[154,15487,15488],{},"Note that during signing you'll need to enter the key's password.",[458,15490,15492],{"id":15491},"_4-framework","4. Framework",[26,15494,15495],{},"In object-oriented programming there's a library that connects all the parts of the system into a single system, this library is called a Framework.",[26,15497,15498],{},"The Android system is written in Java, which is an object-oriented language, so Android also has a Framework. The Android AOSP Framework comes built into apktool's library, but as part of the changes the various manufacturers make to the system, they also make changes to the Framework, so if we want to open Apk files from Samsung, HTC, etc., we'll need to update their Framework files.",[26,15500,15501,15502,15505],{},"To update, you need to extract the framework-res.apk file (from the ",[249,15503,15504],{},"system\u002Fframework\u002F"," directory).",[151,15507,15508,15511],{},[154,15509,15510],{},"On Samsung devices, we'll need the framework-res.apk file and the twframework-res.apk file.",[154,15512,15513],{},"On HTC devices, we'll only need the com.htc.resources.apk file.",[26,15515,15516,15517,126],{},"We place the files in the apktool folder, and to install them we write in Cmd (or the terminal) ",[249,15518,15519],{},"apktool if framework-res.apk",[151,15521,15522,15525],{},[154,15523,15524],{},"If the command is written on Linux, add \u002F. before apktool.",[154,15526,15527],{},"if is short for install framework.",[26,15529,15530,15531,126],{},"If the Framework was installed successfully, we'll get the result: ",[249,15532,15533],{},"I: Framework installed to yourPath 1.apk",[458,15535,15537],{"id":15536},"_5-look-inside","5. Look Inside",[26,15539,15540],{},"Now that we've opened the Apk file we can make a variety of changes. For example, the res folder contains the application's resources, such as images, xml files, etc.",[26,15542,15543,15544,15549,15550,15554],{},"In the example on the left, I opened the Youtube application and went into the res folder, then into the values folder, and opened the settings file with ",[58,15545,15548],{"href":15546,"rel":15547},"http:\u002F\u002Fnotepad-plus-plus.org\u002F",[62],"Notepad++"," (if you use some Linux distribution, I recommend the ",[58,15551,15553],{"href":15114,"rel":15552},[62],"Sublime Text 2"," editor), and translated a few of the buttons (note that you translate what's in black, between the \u003C>, you can see the mark in the image).",[26,15556,15557],{},"In addition, applications are made up of images, whether it's a background, buttons, etc. You can edit them with Photoshop (or Gimp), in the example on the right I opened the Shazam application and colored the app's familiar background (which is an image) red (why? because I can (; ).",[26,15559,15560],{},"And as we said, the system is made up of Apk files, so you can translate the entire system this way (the dialer app, messages, contacts, settings, etc.), if you open (with Winrar or 7Zip) the Rom you downloaded (which is actually a zip file) you'll notice there's a folder called system, and inside it a folder called app where all the Apk files we've talked about are located.",[151,15562,15563,15566,15569],{},[154,15564,15565],{},"In addition, the fonts folder contains the fonts (starting from Android 2.2 there are Hebrew fonts, but you can replace them with a different font if you'd like).",[154,15567,15568],{},"The media folder contains notifications, ringtones, and system files (like the camera's shutter sound, etc.).",[154,15570,15571],{},"The framework folder contains the framework.res.apk file which is responsible for translating the rest of the system (in places that don't belong to specific Apk files like the dialer), for example: the power-off menu, confirmation buttons, lock screen, etc.",[458,15573,4495],{"id":4494},[26,15575,15576],{},"I hope the guide was clear and that you managed to do what you wanted without errors. I highly recommend sharing your results on various forums (you could save time for someone else who wants to make the same change). If you have any questions or ran into any error I'll be happy to try to help in the comments.",{"title":188,"searchDepth":189,"depth":189,"links":15578},[15579,15580,15581,15582,15583,15584],{"id":15301,"depth":189,"text":15302},{"id":15338,"depth":189,"text":15339},{"id":15375,"depth":189,"text":15376},{"id":15491,"depth":189,"text":15492},{"id":15536,"depth":189,"text":15537},{"id":4494,"depth":189,"text":4495},"2012-11-18","How to decompile, edit, rebuild, and re-sign Android Apk files.",{"src":7436},{},"\u002Fblog\u002F2012\u002Fanalyze-an-apk-file",{"title":15065,"description":15586},"blog\u002F2012\u002Fanalyze-an-apk-file","vVKqDeS8edLxhqdJDwrZllCpozNCWM0ETBSKZlCy6-c",{"id":15594,"title":15293,"author":15595,"badge":15597,"body":15598,"date":16234,"description":16235,"draft":193,"extension":194,"image":16236,"meta":16238,"navigation":198,"path":16239,"seo":16240,"stem":16241,"__hash__":16242},"posts\u002Fblog\u002F2012\u002Flittle-android-bit-grammar.md",{"name":16,"to":17,"avatar":15596},{"src":19},{"label":15053},{"type":23,"value":15599,"toc":16218},[15600,15603,15608,15612,15616,15623,15657,15682,15687,15707,15739,15743,15750,15753,15809,15814,15828,15833,15856,15860,15871,15874,15877,15889,15893,15900,15903,15912,15916,15923,15926,15929,15932,15939,15943,15946,15954,15957,15960,15985,15996,16000,16007,16014,16019,16027,16031,16044,16047,16061,16065,16072,16082,16114,16119,16138,16142,16145,16150,16161,16165,16168,16171,16176,16180,16189,16192,16206,16209,16213,16216],[26,15601,15602],{},"In this post I'll survey and explain some of the more advanced concepts (or Grammar) of the Android operating system.",[6344,15604,15605],{},[26,15606,15607],{},"It should be noted that most of the images and dated information are taken from Wikipedia.",[458,15609,15611],{"id":15610},"_1-android-as-an-operating-system","1. Android as an operating system",[4760,15613,15615],{"id":15614},"_11-the-red-layer-linux-kernel-android-isnt-linux","1.1. The red layer: Linux Kernel – (Android isn't Linux)",[26,15617,15618],{},[41,15619],{"alt":15620,"className":15621,"src":15622},"linux kernel",[45,46],"\u002Fposts\u002F2012\u002Flittle-android-bit-grammar\u002Flinux-kernel.webp",[26,15624,7635,15625,15630,15631,15634,15635,15640,15641,15645,15646,15651,15652,983],{},[58,15626,15629],{"href":15627,"rel":15628},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FLinux_kernel",[62],"Linux kernel"," is the kernel used in operating systems of the Linux family. The name \"Linux\" refers to the system's core, the kernel (kernel = the foundation of the system), meaning thousands of lines of code that form the basis of the operating system.",[15632,15633],"br",{},"\nToday it's common to use the name Linux for an entire family of Unix-compatible operating systems that combine the Linux kernel with libraries and tools from the GNU project (",[58,15636,15639],{"href":15637,"rel":15638},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FGNU",[62],"GNU","). Sometimes this also refers to complete Linux distributions (complete operating systems that include web servers such as ",[58,15642,11664],{"href":15643,"rel":15644},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FApache_HTTP_Server",[62],", graphical work environments such as ",[58,15647,15650],{"href":15648,"rel":15649},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FGNOME",[62],"Gnome",", and office application suites such as ",[58,15653,15656],{"href":15654,"rel":15655},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FLibreOffice",[62],"Libre Office",[26,15658,15659,15660,64,15665,64,15670,15675,15676,15681],{},"Because the Linux Kernel is under the GPL license, when Google or OEM manufacturers (like ",[58,15661,15664],{"href":15662,"rel":15663},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHTC",[62],"HTC",[58,15666,15669],{"href":15667,"rel":15668},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMotorola",[62],"SAMSUNG",[58,15671,15674],{"href":15672,"rel":15673},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSamsung",[62],"MOTOROLA",") or chip manufacturers (like ",[58,15677,15680],{"href":15678,"rel":15679},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FQualcomm",[62],"Qualcomm",") integrate their code into the Kernel (intended for use under the Android system), they must also push the code back upstream. This means that any code Google wants to integrate into the Kernel, they'll also integrate into the original project, and therefore everyone will have access to the new piece of code in the Kernel (in other words: Google can't make use of the Linux Kernel, improve it, and keep those improvements only for itself - it has to share the rest).",[26,15683,15684],{},[871,15685,15686],{},"Why the Linux kernel?",[151,15688,15689,15692,15701,15704],{},[154,15690,15691],{},"The price - the Linux kernel is free.",[154,15693,15694,15695,15700],{},"The goal was to create a mobile platform that would run on as many devices as possible, and adapting the Linux kernel to run on ",[58,15696,15699],{"href":15697,"rel":15698},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FARM_architecture",[62],"ARM"," processors from Nvidia, TI, and Qualcomm is relatively easy (since the driver model in Linux is very well understood).",[154,15702,15703],{},"The kernel provides process management, memory management, connectivity, and more, ready out of the box.",[154,15705,15706],{},"The Linux kernel is relatively highly secure and provides a good foundation.",[26,15708,15709,15712,15714,15715,15717,15718,15720,15721,15723,15724,15726,15727,15729,15730,15732,15733,15735,15736,15738],{},[871,15710,15711],{},"The various Android versions are based on different Kernel versions:",[15632,15713],{},"\nAndroid 1.5 - based on Linux kernel 2.6.27.",[15632,15716],{},"\nAndroid 1.6 - based on Linux kernel 2.6.29.",[15632,15719],{},"\nAndroid 2.0\u002F2.1 - based on Linux kernel 2.6.29.",[15632,15722],{},"\nAndroid 2.2 - based on Linux kernel 2.6.32.",[15632,15725],{},"\nAndroid 2.3 - based on Linux kernel 2.6.35.",[15632,15728],{},"\nAndroid 3.0\u002F3.1 - based on Linux kernel 2.6.36.",[15632,15731],{},"\nAndroid 4.0 - based on Linux kernel 3.0.1.",[15632,15734],{},"\nAndroid 4.1 - based on Linux kernel 3.0.31.",[15632,15737],{},"\nAndroid 4.2 - based on Linux kernel 3.4.0.",[4760,15740,15742],{"id":15741},"_12-the-green-layer-the-second-level-libraries-android-runtime","1.2. The green layer, the second level: Libraries + Android Runtime",[26,15744,15745],{},[41,15746],{"alt":15747,"className":15748,"src":15749},"libraries",[45,46],"\u002Fposts\u002F2012\u002Flittle-android-bit-grammar\u002Flibraries.webp",[26,15751,15752],{},"The second (green) layer is made up entirely of open-source libraries. These libraries serve various operating system services, and also developers.",[151,15754,15755,15761,15767,15779,15785,15791,15797,15803],{},[154,15756,15757,15760],{},[871,15758,15759],{},"Media Framework –"," a library that provides essential communication features such as Codecs, various video and image formats (AAC, MP3, H.264, MPEG4, JPG, PNG), etc.",[154,15762,15763,15766],{},[871,15764,15765],{},"SGL –"," a basic 2D graphics engine.",[154,15768,15769,15772,15773,15778],{},[871,15770,15771],{},"OpenGL \u002F ES –"," a set of 3D interface programs and applications (",[58,15774,15777],{"href":15775,"rel":15776},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FApplication_programming_interface",[62],"API",") capable of performing 3D hardware acceleration.",[154,15780,15781,15784],{},[871,15782,15783],{},"WebKit –"," the WebKit browser libraries, an open-source browser supporting, among other things, JavaScript and CSS.",[154,15786,15787,15790],{},[871,15788,15789],{},"SSL –"," communication protocols enabling secure and encrypted communication between two applications.",[154,15792,15793,15796],{},[871,15794,15795],{},"Surface Manager -"," a library enabling access to the \"front-facing\" device driver library of the device's display.",[154,15798,15799,15802],{},[871,15800,15801],{},"SQLite –"," enables reading and writing directly from a database on disk (without a mediating server process).",[154,15804,15805,15808],{},[871,15806,15807],{},"Bionic -"," a libc library adapted for Android (due to licensing issues with the libc library).",[26,15810,15811],{},[871,15812,15813],{},"Android Runtime",[151,15815,15816,15822],{},[154,15817,15818,15821],{},[871,15819,15820],{},"Core Libs -"," a library containing the core original source code of the Android operating system (written in Java).",[154,15823,15824,15827],{},[871,15825,15826],{},"Dalvik VM -"," Dalvik is a virtual machine written from scratch specifically for the Android system, intended for running Apk files.",[26,15829,15830],{},[871,15831,15832],{},"Why create a new virtual machine (Dalvik) instead of using a Java VM?",[866,15834,15835,15844,15847],{},[154,15836,15837,15838,15843],{},"Java VM costs money - it's owned by Oracle. If every manufacturer were required to go to ",[58,15839,15842],{"href":15840,"rel":15841},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FOracle_Corporation",[62],"Oracle"," to negotiate the cost of using a Java VM, the Android platform wouldn't achieve its goal (to be a free mobile platform that runs on as many devices as possible).",[154,15845,15846],{},"Over the years, the Java VM developed around Intel processors, since that's mostly what was in computers and business servers, and therefore it isn't well suited to run on ARM processors (at least not in its current form).",[154,15848,15849,15850,15855],{},"The Java VM loads the software into memory (from the hard disk) and only then creates the object, which makes sense, since the hard disk is much slower than ",[58,15851,15854],{"href":15852,"rel":15853},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FRandom-access_memory",[62],"RAM",". Dalvik doesn't do this, since mobile devices don't have a hard disk - there's RAM and flash storage (very fast, unlike a mechanical hard disk, so there's no reason to do that).",[13866,15857,15859],{"id":15858},"_121-dalvik","1.2.1 Dalvik",[26,15861,15862,15865,15867,15868,15870],{},[871,15863,15864],{},"Let's analyze the situations when we write for Dalvik vs. for the Java VM:",[15632,15866],{},"\nWhen we write for the Java VM, we write in the Java language, the Java compiler compiles the code into bytecode (a hybrid of half programming language and half machine language), and we get a class file, and that file will run on the Java VM.",[15632,15869],{},"\nWhen we write for Dalvik, we write in the Java language, the Java compiler compiles the code into bytecode, and we get a class file. Then the Dalvik compiler compiles the bytecode again, and we get a dex file - that file will run on the Dalvik VM.",[26,15872,15873],{},"In Java, we collect all the class files into a Jar file and then compress it as a zip file. In Android, we don't do this, but even as uncompressed code, code suited to Dalvik will be smaller than Java code, and that's because the Dalvik compiler performs very high optimization (identifies duplicate code, can change data structures and libraries as needed, changes byte order, etc.). As a result of all this, Dalvik uses less memory than the Java VM.",[26,15875,15876],{},"For anyone interested in going deeper, here's an hour-long video from Google I\u002FO 2008 explaining in detail how Dalvik works:",[15878,15879,15883],"div",{"className":15880},[15881,15882],"flex","justify-center",[15884,15885],"iframe",{"width":15886,"height":15887,"src":15888,"frameBorder":11631,"allowFullScreen":198},420,315,"https:\u002F\u002Fwww.youtube.com\u002Fembed\u002FptjedOZEXPM",[4760,15890,15892],{"id":15891},"_13-the-blue-layer-applications-framework","1.3. The blue layer: Applications Framework",[26,15894,15895],{},[41,15896],{"alt":15897,"className":15898,"src":15899},"application framework",[45,46],"\u002Fposts\u002F2012\u002Flittle-android-bit-grammar\u002Fapplication-framework.webp",[26,15901,15902],{},"The third level is called the Application Framework, and in other words can also be called the API Level.",[26,15904,15905,15906,15911],{},"This is essentially the system's skeleton - the Framework exposes the system's capabilities to applications. When a developer sets the app's API Level, they're essentially determining what will be possible (or accessible) at this layer, and as a result, which Android versions the app can be installed on. For example, at API Level 10 (Android version 2.3.3), it became possible for the first time to make use of the ",[58,15907,15910],{"href":15908,"rel":15909},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FNear_field_communication",[62],"NFC"," component, and only apps with minimum support for API Level 10 and above will be able to make use of this component.",[4760,15913,15915],{"id":15914},"_14-the-blue-layer-applications","1.4. The blue layer: Applications",[26,15917,15918],{},[41,15919],{"alt":15920,"className":15921,"src":15922},"applications",[45,46],"\u002Fposts\u002F2012\u002Flittle-android-bit-grammar\u002Fapplications.webp",[26,15924,15925],{},"In the fourth and final layer, we find all the built-in, basic apps of the system (for example: the dialer, contacts, messages, browser, launcher, etc.).",[26,15927,15928],{},"An APK file (or app) is essentially a collection of dex files (code suited to run on the Dalvik VM), resources (image files, video files, audio files, XML files, etc.), and possibly also some native libs.",[26,15930,15931],{},"All the layers together, as Android is built:",[26,15933,15934],{},[41,15935],{"alt":15936,"className":15937,"src":15938},"android architecture",[45,46],"\u002Fposts\u002F2012\u002Flittle-android-bit-grammar\u002Fandroid-architecture.webp",[458,15940,15942],{"id":15941},"_2-adb","2. ADB",[26,15944,15945],{},"ADB (short for Android Debug Bridge) is a program run from the command line (cmd \u002F terminal) on the computer that communicates with the device or emulator (in other words, the adb program opens a command line to the device).",[15878,15947,9797,15950],{"className":15948},[15949],"left",[41,15951],{"src":15952,"alt":15953},"\u002Fimages\u002Fposts\u002Fandroid-grammar\u002FAndroid_ADB.png","Android ADB",[26,15955,15956],{},"adb is used by developers to manage the Android system directly from the computer. It acts as a driver between the device and the computer and adds commands to the terminal (\u002Fcmd) that give developers the ability to perform various operations, such as instructing the device to enter certain states, instructing it to install a specific app (located on the computer and not on the device), etc.",[26,15958,15959],{},"When we open ADB (it can be opened from the sdk\u002Fplatform-tools folder), it first checks whether an ADB process is already open; if not, it starts the process. When ADB comes up, it connects to TCP port 5037 and listens for commands from ADB (the command line). The server searches for and establishes connections to every emulator\u002Fdevice in the port range 5555 to 5585. Each emulator\u002Fdevice \"acquires\" a pair of ports, for example:",[242,15961,15963],{"className":15127,"code":15962,"language":15130,"meta":188,"style":188},"Emulator 1, console: 5554\nEmulator 1, adb: 5555\nEmulator 2, console: 5556\nEmulator 2, adb: 5557\n",[249,15964,15965,15970,15975,15980],{"__ignoreMap":188},[252,15966,15967],{"class":254,"line":255},[252,15968,15969],{},"Emulator 1, console: 5554\n",[252,15971,15972],{"class":254,"line":189},[252,15973,15974],{},"Emulator 1, adb: 5555\n",[252,15976,15977],{"class":254,"line":326},[252,15978,15979],{},"Emulator 2, console: 5556\n",[252,15981,15982],{"class":254,"line":343},[252,15983,15984],{},"Emulator 2, adb: 5557\n",[26,15986,15987,15988,15990,15995],{},"When opening ADB and connecting to a device, what happens in the background on the device is the opening of a Linux Shell, and therefore a variety of operations can be performed by writing commands directly to the device, similar to a terminal in Linux.",[15632,15989],{},[58,15991,15994],{"href":15992,"rel":15993},"http:\u002F\u002Fandroid-dls.com\u002Fwiki\u002Findex.php?title=ADB",[62],"Here"," you can find a variety of commands that can be used in ADB (with explanations).",[458,15997,15999],{"id":15998},"_3-bootloader","3. Bootloader",[15878,16001,9797,16003],{"className":16002},[15949],[41,16004],{"src":16005,"alt":16006},"\u002Fimages\u002Fposts\u002Fandroid-grammar\u002FBootloader.png","Bootloader",[26,16008,7635,16009,16013],{},[58,16010,16006],{"href":16011,"rel":16012},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FBooting",[62]," isn't really directly related to the Android operating system - rather it's a special software component that runs while the system boots up (after the device powers on and before the Android system loads). Its job is to perform memory checks and load the operating system (OS) into RAM, and \"hand over the reins\" of the device to the operating system.",[151,16015,16016],{},[154,16017,16018],{},"The Bootloader is often compared to the Bios software we know from the PC world, where the Bios is essentially a unit completely separate from the operating system that comes up during boot, before the operating system (OS) loads.",[15878,16020,9797,16022],{"className":16021},[15949],[41,16023],{"src":16024,"alt":16025,"style":16026},"\u002Fimages\u002Fposts\u002Fandroid-grammar\u002FClockworkmod_Recovery.jpg","Clockworkmod Recovery","width: 15rem;",[458,16028,16030],{"id":16029},"_4-recovery","4. Recovery",[26,16032,16033,16034,64,16039,126],{},"The word Recovery refers to a separate partition where the recovery console is installed (which can be reached via a combination of several buttons, the combination being device-dependent). The recovery included in the Android system is, unfortunately, very simple in its options, but it's relatively easy to obtain its source code and add (develop) additional options. Thanks to the talented developers who flood the Android community, there are several recoveries that do this. The most common of them are: ",[58,16035,16038],{"href":16036,"rel":16037},"https:\u002F\u002Fwww.clockworkmod.com\u002F",[62],"Clockwork Recovery",[58,16040,16043],{"href":16041,"rel":16042},"http:\u002F\u002Fwww.teamw.in\u002F",[62],"OpenRecovery",[26,16045,16046],{},"The recovery built into the Android system is intentionally limited. It's meant to perform specific operations and therefore offers only the following 2 options: wipe all system and user files, and install system updates. Whereas the recovery developed by various developers usually offers a variety of additional options such as:",[151,16048,16049,16052,16055,16058],{},[154,16050,16051],{},"Backup and restore option.",[154,16053,16054],{},"Deleting specific partitions.",[154,16056,16057],{},"Installing packages not signed by official parties.",[154,16059,16060],{},"The ability to mount various partitions.",[458,16062,16064],{"id":16063},"_5-apk","5. APK",[15878,16066,9797,16068],{"className":16067},[15949],[41,16069],{"src":16070,"alt":16071},"\u002Fimages\u002Fposts\u002Fandroid-grammar\u002FApk.png","Apk",[26,16073,16074,16078,16079,16081],{},[58,16075,16071],{"href":16076,"rel":16077},"http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FAndroid_application_package",[62]," is the file extension in which Android system apps are saved. You can compare the extension to the bat, jar, exe extensions, etc. The file itself is a completely regular zip file (you can change its extension from app.apk to app.zip and open it normally).",[15632,16080],{},"\nIf we open it, we'll see a number of folders and files - let's try to understand what's going on here:",[151,16083,16084,16090,16096,16102,16108],{},[154,16085,16086,16089],{},[871,16087,16088],{},"META-INF:"," This library contains the signature of the zip file. Without the file's signature, you can't install the apk (the app), because the signature contains the file's 'ID card'. During installation, the system checks whether the signature matches and gives an error message if one of the files is different. It should be noted that the file can be re-signed.",[154,16091,16092,16095],{},[871,16093,16094],{},"res:"," This folder contains the resources (files) for the app's use, for example: images, xml files, etc.",[154,16097,16098,16101],{},[871,16099,16100],{},"AndroidManifest.xml:"," A file required for the app's installation on the device to succeed. The file contains the app's details (app name, version, permissions, etc.).",[154,16103,16104,16107],{},[871,16105,16106],{},"classes.dex:"," A file containing the classes the programmer used to write the app. This file is written in Java bytecode (compiled Java) and is fully interpreted by the Dalvik virtual machine.",[154,16109,16110,16113],{},[871,16111,16112],{},"resources.arsc:"," A compressed (and compiled) file containing the resources.",[26,16115,16116],{},[871,16117,16118],{},"You can install the apk file in two ways:",[866,16120,16121,16135],{},[154,16122,16123,16124,1894,16129,16134],{},"Copy it to the device (internal memory \u002F SD card) and enter the folder (with a file manager such as ",[58,16125,16128],{"href":16126,"rel":16127},"https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.metago.astro&hl=en",[62],"Astro",[58,16130,16133],{"href":16131,"rel":16132},"https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.estrongs.android.pop&hl=en",[62],"ES File Explorer",") and install it (make sure \"Unknown sources\" is checked under Settings -> Applications).",[154,16136,16137],{},"Install it via ADB (make sure the device is connected to the computer via a USB cable and that under Settings -> Applications -> Development -> USB debugging is checked).",[458,16139,16141],{"id":16140},"_6-root","6. Root",[26,16143,16144],{},"Concept: granting system administrator permissions on the device (SuperUser). Like Administrator on Windows, so that we can access and play with things that aren't accessible to a regular user's permissions (like the System folder in our case) (the concept comes from the Linux world, where the system folder is called root, and is marked with \u002F). The System folder is protected, and to change things there we need that same permission. Usually regular users have no need for root, but developers, hackers, and other curious folks who want to change things must have this permission.",[26,16146,16147],{},[871,16148,16149],{},"Examples of why that permission (Root) is needed:",[866,16151,16152,16155,16158],{},[154,16153,16154],{},"Until Android version 2.2 (Froyo), Android had no Hebrew support, and to add Hebrew fonts to the device, system files needed to be modified, so SuperUser permissions were required, and to obtain those permissions we needed to root the device.",[154,16156,16157],{},"Also, there are apps that require Root, such as: screen capture (except on some devices), CPU overclocking, repartitioning memory partitions, installing a different ROM, and more.",[154,16159,16160],{},"Even though we mentioned that these permissions usually aren't needed by regular users, the process is almost always very easy, and most users perform it in order to \"gain\" full freedom.",[458,16162,16164],{"id":16163},"_7-rom","7. Rom",[26,16166,16167],{},"Rom = the operating system. (Not Windows Phone 7, Android, or iOS, but different versions\u002Fdistributions of Android), (similar to the various distributions of Linux).",[26,16169,16170],{},"The rom \"arrives\" as an img file (often it arrives with additions, packed in a zip file); the file is uploaded to the memory card and installed via recovery (or via download mode on Samsung devices). Android is an open operating system, which means everyone can obtain the source code and make changes as they see fit. Following on from that, there are developer groups who release their own roms (operating systems in their own versions) after changes they've made, such as improving battery life, improving device performance, removing built-in apps that aren't in use, replacing apps with 'lighter' versions, graphical changes, and more.",[151,16172,16173],{},[154,16174,16175],{},"In the past (and unfortunately it still happens today), there were cases where manufacturers abandoned devices and stopped releasing updates for them (for example, Samsung with the i7500 device). The developer community often takes care of those users and releases roms with newer Android versions or new interfaces (like what happened with the Sense 3 interface, on HTC's Desire device).",[458,16177,16179],{"id":16178},"_8-odexed-deodexed","8. Odexed & Deodexed",[26,16181,16182,16183,16185,16186,16188],{},"We've all come across this term, but what does it actually mean?",[15632,16184],{},"\nI assume you know that the Android system has various partitions, like the system\u002F partition which contains all the system apps (which can't be modified or deleted), and the data\u002F partition which contains user apps (which can be modified and deleted).",[15632,16187],{},"\nWhen the device boots up, the system\u002F partition is the first to load, and thus apps found in this partition are given preference for being in memory.",[26,16190,16191],{},"There are two tracks by which the system can work:",[151,16193,16194,16200],{},[154,16195,16196,16199],{},[871,16197,16198],{},"Odexed -"," the cache of each app is held in a separate file called odex, and is loaded into the virtual machine while the device boots, which reduces the time needed for the device to boot.",[154,16201,16202,16205],{},[871,16203,16204],{},"Deodexed -"," the cache of each app is held inside the Apk itself, in a file called classes.dex, and increases device boot time.",[26,16207,16208],{},"Most manufacturers (OEMs) choose the first option, since it improves device boot times, because the cache is built as part of the VM itself, whereas in the second option the cache loads when the app (the apk) is opened. But Deodexed roms allow changing the look and feel of built-in system apps, and so this is a very popular option despite everything. In addition, in practice, only the device's boot time the first time (after clearing the Dalvik cache) is longer than usual.",[458,16210,16212],{"id":16211},"_9-summary","9. Summary",[26,16214,16215],{},"We went over the Android system's architecture and the various libraries used at each layer, gave an explanation for the choice of the Linux kernel, and for building a unique VM (Dalvik). From there we moved on to the system's basic concepts such as ADB, Bootloader, Recovery, apk, Root, and Rom. And finally, we also touched on a more advanced concept: the difference between Odexed and Deodexed apps.",[772,16217,15261],{},{"title":188,"searchDepth":189,"depth":189,"links":16219},[16220,16226,16227,16228,16229,16230,16231,16232,16233],{"id":15610,"depth":189,"text":15611,"children":16221},[16222,16223,16224,16225],{"id":15614,"depth":326,"text":15615},{"id":15741,"depth":326,"text":15742},{"id":15891,"depth":326,"text":15892},{"id":15914,"depth":326,"text":15915},{"id":15941,"depth":189,"text":15942},{"id":15998,"depth":189,"text":15999},{"id":16029,"depth":189,"text":16030},{"id":16063,"depth":189,"text":16064},{"id":16140,"depth":189,"text":16141},{"id":16163,"depth":189,"text":16164},{"id":16178,"depth":189,"text":16179},{"id":16211,"depth":189,"text":16212},"2012-09-16","A tour of Android's architecture: kernel, Dalvik, ADB, bootloader, and root.",{"src":16237},"\u002Fposts\u002F2012\u002Flittle-android-bit-grammar\u002Fcover.webp",{},"\u002Fblog\u002F2012\u002Flittle-android-bit-grammar",{"title":15293,"description":16235},"blog\u002F2012\u002Flittle-android-bit-grammar","pSP3QoKwuO1QJVuigyOsClo_fGx_d842Z8L7jhfd0l8",1786001638385]